diff options
31 files changed, 546 insertions, 125 deletions
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index 7a3c95e004c5..8eca43158ef2 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -26,6 +26,8 @@ import android.graphics.drawable.AnimatedVectorDrawable; import dalvik.annotation.optimization.FastNative; +import libcore.util.NativeAllocationRegistry; + /** * <p>A display list records a series of graphics related operations and can replay * them later. Display lists are usually built by recording operations on a @@ -130,13 +132,20 @@ import dalvik.annotation.optimization.FastNative; */ public class RenderNode { + // Use a Holder to allow static initialization in the boot image. + private static class NoImagePreloadHolder { + public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( + RenderNode.class.getClassLoader(), nGetNativeFinalizer(), 1024); + } + private boolean mValid; // Do not access directly unless you are ThreadedRenderer - long mNativeRenderNode; + final long mNativeRenderNode; private final View mOwningView; private RenderNode(String name, View owningView) { mNativeRenderNode = nCreate(name); + NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode); mOwningView = owningView; } @@ -145,6 +154,7 @@ public class RenderNode { */ private RenderNode(long nativePtr) { mNativeRenderNode = nativePtr; + NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativeRenderNode); mOwningView = null; } @@ -154,19 +164,7 @@ public class RenderNode { * is not feasible. */ public void destroy() { - if (mNativeRenderNode != 0) { - nFinalize(mNativeRenderNode); - mNativeRenderNode = 0; - } - } - - @Override - protected void finalize() throws Throwable { - try { - destroy(); - } finally { - super.finalize(); - } + // TODO: Removed temporarily } /** @@ -835,7 +833,6 @@ public class RenderNode { // Intentionally not static because it acquires a reference to 'this' private native long nCreate(String name); - private native void nFinalize(long renderNode); private static native long nGetNativeFinalizer(); private static native void nSetDisplayList(long renderNode, long newData); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 5eaabe7c137b..541fbe0528f1 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -111,6 +111,8 @@ import android.widget.TextView.Drawables; import android.widget.TextView.OnEditorActionListener; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; import com.android.internal.util.Preconditions; @@ -1119,14 +1121,26 @@ public class Editor { getInsertionController().show(); mIsInsertionActionModeStartPending = true; handled = true; + MetricsLogger.action( + mTextView.getContext(), + MetricsEvent.TEXT_LONGPRESS, + TextViewMetrics.SUBTYPE_LONG_PRESS_OTHER); } if (!handled && mTextActionMode != null) { if (touchPositionIsInSelection()) { startDragAndDrop(); + MetricsLogger.action( + mTextView.getContext(), + MetricsEvent.TEXT_LONGPRESS, + TextViewMetrics.SUBTYPE_LONG_PRESS_DRAG_AND_DROP); } else { stopTextActionMode(); selectCurrentWordAndStartDrag(); + MetricsLogger.action( + mTextView.getContext(), + MetricsEvent.TEXT_LONGPRESS, + TextViewMetrics.SUBTYPE_LONG_PRESS_SELECTION); } handled = true; } @@ -1134,6 +1148,12 @@ public class Editor { // Start a new selection if (!handled) { handled = selectCurrentWordAndStartDrag(); + if (handled) { + MetricsLogger.action( + mTextView.getContext(), + MetricsEvent.TEXT_LONGPRESS, + TextViewMetrics.SUBTYPE_LONG_PRESS_SELECTION); + } } return handled; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 5426a37cdd80..69f463cdd856 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -148,6 +148,8 @@ import android.view.textservice.TextServicesManager; import android.widget.RemoteViews.RemoteView; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FastMath; import com.android.internal.widget.EditableInputConnection; @@ -9685,6 +9687,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (handled) { performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); if (mEditor != null) mEditor.mDiscardNextActionUp = true; + } else { + MetricsLogger.action( + mContext, + MetricsEvent.TEXT_LONGPRESS, + TextViewMetrics.SUBTYPE_LONG_PRESS_OTHER); } return handled; diff --git a/core/java/android/widget/TextViewMetrics.java b/core/java/android/widget/TextViewMetrics.java new file mode 100644 index 000000000000..0a14d3e8b466 --- /dev/null +++ b/core/java/android/widget/TextViewMetrics.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +/** + * {@link com.android.internal.logging.MetricsLogger} values for TextView. + * + * @hide + */ +final class TextViewMetrics { + + private TextViewMetrics() {} + + /** + * Long press on TextView - no special classification. + */ + static final int SUBTYPE_LONG_PRESS_OTHER = 0; + /** + * Long press on TextView - selection started. + */ + static final int SUBTYPE_LONG_PRESS_SELECTION = 1; + /** + * Long press on TextView - drag and drop started. + */ + static final int SUBTYPE_LONG_PRESS_DRAG_AND_DROP = 2; +} diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index e10fdbdb2845..34568391167d 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -915,6 +915,16 @@ namespace PaintGlue { paint->setLetterSpacing(letterSpacing); } + static jfloat getWordSpacing(jlong paintHandle) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + return paint->getWordSpacing(); + } + + static void setWordSpacing(jlong paintHandle, jfloat wordSpacing) { + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + paint->setWordSpacing(wordSpacing); + } + static jint getHyphenEdit(jlong paintHandle, jint hyphen) { Paint* paint = reinterpret_cast<Paint*>(paintHandle); return paint->getHyphenEdit(); @@ -1043,6 +1053,8 @@ static const JNINativeMethod methods[] = { {"nSetTextSkewX","(JF)V", (void*) PaintGlue::setTextSkewX}, {"nGetLetterSpacing","(J)F", (void*) PaintGlue::getLetterSpacing}, {"nSetLetterSpacing","(JF)V", (void*) PaintGlue::setLetterSpacing}, + {"nGetWordSpacing","(J)F", (void*) PaintGlue::getWordSpacing}, + {"nSetWordSpacing","(JF)V", (void*) PaintGlue::setWordSpacing}, {"nGetHyphenEdit", "(J)I", (void*) PaintGlue::getHyphenEdit}, {"nSetHyphenEdit", "(JI)V", (void*) PaintGlue::setHyphenEdit}, {"nAscent","(JJ)F", (void*) PaintGlue::ascent}, diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index f88de51a9a12..dd2a7a98b7a0 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -124,10 +124,6 @@ static void releaseRenderNode(RenderNode* renderNode) { renderNode->decStrong(0); } -static void android_view_RenderNode_finalize(JNIEnv* env, jobject clazz, jlong renderNodePtr) { - releaseRenderNode(reinterpret_cast<RenderNode*>(renderNodePtr)); -} - static jlong android_view_RenderNode_getNativeFinalizer(JNIEnv* env, jobject clazz) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(&releaseRenderNode)); @@ -654,7 +650,6 @@ static const JNINativeMethod gMethods[] = { // Regular JNI // ---------------------------------------------------------------------------- { "nCreate", "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create }, - { "nFinalize", "(J)V", (void*) android_view_RenderNode_finalize }, { "nGetNativeFinalizer", "()J", (void*) android_view_RenderNode_getNativeFinalizer }, { "nSetDisplayList", "(JJ)V", (void*) android_view_RenderNode_setDisplayList }, { "nOutput", "(J)V", (void*) android_view_RenderNode_output }, diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 98d45dc33ead..554e5d2614dd 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1442,6 +1442,28 @@ public class Paint { } /** + * Return the paint's word-spacing for text. The default value is 0. + * + * @return the paint's word-spacing for drawing text. + * @hide + */ + public float getWordSpacing() { + return nGetWordSpacing(mNativePaint); + } + + /** + * Set the paint's word-spacing for text. The default value is 0. + * The value is in pixels (note the units are not the same as for + * letter-spacing). + * + * @param wordSpacing set the paint's word-spacing for drawing text. + * @hide + */ + public void setWordSpacing(float wordSpacing) { + nSetWordSpacing(mNativePaint, wordSpacing); + } + + /** * Returns the font feature settings. The format is the same as the CSS * font-feature-settings attribute: * <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop"> @@ -2711,6 +2733,10 @@ public class Paint { @CriticalNative private static native void nSetLetterSpacing(long paintPtr, float letterSpacing); @CriticalNative + private static native float nGetWordSpacing(long paintPtr); + @CriticalNative + private static native void nSetWordSpacing(long paintPtr, float wordSpacing); + @CriticalNative private static native int nGetHyphenEdit(long paintPtr); @CriticalNative private static native void nSetHyphenEdit(long paintPtr, int hyphen); diff --git a/graphics/tests/graphicstests/src/android/graphics/PaintTest.java b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java index 6763dd1970ae..318bfb6bfd39 100644 --- a/graphics/tests/graphicstests/src/android/graphics/PaintTest.java +++ b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java @@ -218,4 +218,13 @@ public class PaintTest extends AndroidTestCase { assertEquals(width, p.measureText(bidiText), 1.0f); } } + + public void testSetGetWordSpacing() { + Paint p = new Paint(); + assertEquals(0.0f, p.getWordSpacing()); // The default value should be 0. + p.setWordSpacing(1.0f); + assertEquals(1.0f, p.getWordSpacing()); + p.setWordSpacing(-2.0f); + assertEquals(-2.0f, p.getWordSpacing()); + } } diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 47220508f2bd..fdf4d52f357b 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -24,6 +24,7 @@ hwui_src_files := \ pipeline/skia/ReorderBarrierDrawables.cpp \ pipeline/skia/SkiaDisplayList.cpp \ pipeline/skia/SkiaOpenGLPipeline.cpp \ + pipeline/skia/SkiaOpenGLReadback.cpp \ pipeline/skia/SkiaPipeline.cpp \ pipeline/skia/SkiaProfileRenderer.cpp \ pipeline/skia/SkiaRecordingCanvas.cpp \ @@ -84,6 +85,7 @@ hwui_src_files := \ LayerUpdateQueue.cpp \ Matrix.cpp \ OpDumper.cpp \ + OpenGLReadback.cpp \ Patch.cpp \ PatchCache.cpp \ PathCache.cpp \ @@ -96,7 +98,6 @@ hwui_src_files := \ Properties.cpp \ PropertyValuesAnimatorSet.cpp \ PropertyValuesHolder.cpp \ - Readback.cpp \ RecordingCanvas.cpp \ RenderBufferCache.cpp \ RenderNode.cpp \ diff --git a/libs/hwui/Readback.cpp b/libs/hwui/OpenGLReadback.cpp index 1645218495eb..da6d994f436c 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/OpenGLReadback.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "Readback.h" +#include "OpenGLReadback.h" #include "Caches.h" #include "Image.h" @@ -31,8 +31,69 @@ namespace android { namespace uirenderer { -static CopyResult copyTextureInto(Caches& caches, RenderState& renderState, - Texture& sourceTexture, Matrix4& texTransform, const Rect& srcRect, +CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect, + SkBitmap* bitmap) { + ATRACE_CALL(); + mRenderThread.eglManager().initialize(); + + // Setup the source + sp<GraphicBuffer> sourceBuffer; + sp<Fence> sourceFence; + Matrix4 texTransform; + status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, + texTransform.data); + texTransform.invalidateType(); + if (err != NO_ERROR) { + ALOGW("Failed to get last queued buffer, error = %d", err); + return CopyResult::UnknownError; + } + if (!sourceBuffer.get()) { + ALOGW("Surface doesn't have any previously queued frames, nothing to readback from"); + return CopyResult::SourceEmpty; + } + if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) { + ALOGW("Surface is protected, unable to copy from it"); + return CopyResult::SourceInvalid; + } + err = sourceFence->wait(500 /* ms */); + if (err != NO_ERROR) { + ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt"); + return CopyResult::Timeout; + } + + // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via + // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES + // to be able to properly sample from the buffer. + + // Create the EGLImage object that maps the GraphicBuffer + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + EGLClientBuffer clientBuffer = (EGLClientBuffer) sourceBuffer->getNativeBuffer(); + EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; + + EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); + + if (sourceImage == EGL_NO_IMAGE_KHR) { + ALOGW("eglCreateImageKHR failed (%#x)", eglGetError()); + return CopyResult::UnknownError; + } + + CopyResult copyResult = copyImageInto(sourceImage, texTransform, sourceBuffer->getWidth(), + sourceBuffer->getHeight(), srcRect, bitmap); + + // All we're flushing & finishing is the deletion of the texture since + // copyImageInto already did a major flush & finish as an implicit + // part of glReadPixels, so this shouldn't pose any major stalls. + glFinish(); + eglDestroyImageKHR(display, sourceImage); + return copyResult; +} + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, + Texture& sourceTexture, const Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap) { int destWidth = bitmap->width(); int destHeight = bitmap->height(); @@ -134,88 +195,40 @@ static CopyResult copyTextureInto(Caches& caches, RenderState& renderState, return CopyResult::Success; } -CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, - Surface& surface, const Rect& srcRect, SkBitmap* bitmap) { - ATRACE_CALL(); - renderThread.eglManager().initialize(); +CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage, + const Matrix4& imgTransform, int imgWidth, int imgHeight, const Rect& srcRect, + SkBitmap* bitmap) { Caches& caches = Caches::getInstance(); - - // Setup the source - sp<GraphicBuffer> sourceBuffer; - sp<Fence> sourceFence; - Matrix4 texTransform; - status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, - texTransform.data); - texTransform.invalidateType(); - if (err != NO_ERROR) { - ALOGW("Failed to get last queued buffer, error = %d", err); - return CopyResult::UnknownError; - } - if (!sourceBuffer.get()) { - ALOGW("Surface doesn't have any previously queued frames, nothing to readback from"); - return CopyResult::SourceEmpty; - } - if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) { - ALOGW("Surface is protected, unable to copy from it"); - return CopyResult::SourceInvalid; - } - err = sourceFence->wait(500 /* ms */); - if (err != NO_ERROR) { - ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt"); - return CopyResult::Timeout; - } - - // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via - // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES - // to be able to properly sample from the buffer. - - // Create the EGLImage object that maps the GraphicBuffer - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EGLClientBuffer clientBuffer = (EGLClientBuffer) sourceBuffer->getNativeBuffer(); - EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; - - EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, - EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); - - if (sourceImage == EGL_NO_IMAGE_KHR) { - ALOGW("eglCreateImageKHR failed (%#x)", eglGetError()); - return CopyResult::UnknownError; - } GLuint sourceTexId; // Create a 2D texture to sample from the EGLImage glGenTextures(1, &sourceTexId); caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId); - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, sourceImage); + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage); GLenum status = GL_NO_ERROR; while ((status = glGetError()) != GL_NO_ERROR) { ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status); - eglDestroyImageKHR(display, sourceImage); return CopyResult::UnknownError; } Texture sourceTexture(caches); - sourceTexture.wrap(sourceTexId, sourceBuffer->getWidth(), - sourceBuffer->getHeight(), 0, 0 /* total lie */, GL_TEXTURE_EXTERNAL_OES); + sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */, + GL_TEXTURE_EXTERNAL_OES); - CopyResult copyResult = copyTextureInto(caches, renderThread.renderState(), - sourceTexture, texTransform, srcRect, bitmap); + CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(), + sourceTexture, imgTransform, srcRect, bitmap); sourceTexture.deleteTexture(); - // All we're flushing & finishing is the deletion of the texture since - // copyTextureInto already did a major flush & finish as an implicit - // part of glReadPixels, so this shouldn't pose any major stalls. - glFinish(); - eglDestroyImageKHR(display, sourceImage); return copyResult; } -CopyResult Readback::copyTextureLayerInto(renderthread::RenderThread& renderThread, +bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread, Layer& layer, SkBitmap* bitmap) { - ATRACE_CALL(); - return copyTextureInto(Caches::getInstance(), renderThread.renderState(), - layer.getTexture(), layer.getTexTransform(), Rect(), bitmap); + return CopyResult::Success == copyTextureInto(Caches::getInstance(), + renderThread.renderState(), layer.getTexture(), layer.getTexTransform(), + Rect(), bitmap); } + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/OpenGLReadback.h b/libs/hwui/OpenGLReadback.h new file mode 100644 index 000000000000..7ec2a96a9ac6 --- /dev/null +++ b/libs/hwui/OpenGLReadback.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "Readback.h" + +namespace android { +namespace uirenderer { + +class Matrix4; +class Layer; + +class OpenGLReadback : public Readback { +public: + virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, + SkBitmap* bitmap) override; + +protected: + explicit OpenGLReadback(renderthread::RenderThread& thread) : Readback(thread) {} + virtual ~OpenGLReadback() {} + + virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, + int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) = 0; +}; + +class OpenGLReadbackImpl : public OpenGLReadback { +public: + OpenGLReadbackImpl(renderthread::RenderThread& thread) : OpenGLReadback(thread) {} + + /** + * Copies the layer's contents into the provided bitmap. + */ + static bool copyLayerInto(renderthread::RenderThread& renderThread, Layer& layer, + SkBitmap* bitmap); + +protected: + virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, + int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override; +}; + +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/Readback.h b/libs/hwui/Readback.h index 55c943c0ebee..7fbc4bf48c4e 100644 --- a/libs/hwui/Readback.h +++ b/libs/hwui/Readback.h @@ -25,8 +25,6 @@ namespace android { namespace uirenderer { -class Layer; - // Keep in sync with PixelCopy.java codes enum class CopyResult { Success = 0, @@ -42,15 +40,14 @@ public: /** * Copies the surface's most recently queued buffer into the provided bitmap. */ - static CopyResult copySurfaceInto(renderthread::RenderThread& renderThread, - Surface& surface, const Rect& srcRect, SkBitmap* bitmap); + virtual CopyResult copySurfaceInto(Surface& surface, const Rect& srcRect, + SkBitmap* bitmap) = 0; - /** - * Copies the TextureLayer's texture content (thus, the currently rendering buffer) into the - * provided bitmap. - */ - static CopyResult copyTextureLayerInto(renderthread::RenderThread& renderThread, - Layer& layer, SkBitmap* bitmap); +protected: + explicit Readback(renderthread::RenderThread& thread) : mRenderThread(thread) {} + virtual ~Readback() {} + + renderthread::RenderThread& mRenderThread; }; } // namespace uirenderer diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index a06cc37f944e..8dd165c46d21 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -45,6 +45,7 @@ minikin::FontStyle MinikinUtils::prepareMinikinPaint(minikin::MinikinPaint* mini minikinPaint->scaleX = paint->getTextScaleX(); minikinPaint->skewX = paint->getTextSkewX(); minikinPaint->letterSpacing = paint->getLetterSpacing(); + minikinPaint->wordSpacing = paint->getWordSpacing(); minikinPaint->paintFlags = MinikinFontSkia::packPaintFlags(paint); minikinPaint->fontFeatureSettings = paint->getFontFeatureSettings(); minikinPaint->hyphenEdit = minikin::HyphenEdit(paint->getHyphenEdit()); diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index 10a1db9ace3d..c9b5f0031a7b 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -48,6 +48,14 @@ public: return mLetterSpacing; } + void setWordSpacing(float wordSpacing) { + mWordSpacing = wordSpacing; + } + + float getWordSpacing() const { + return mWordSpacing; + } + void setFontFeatureSettings(const std::string& fontFeatureSettings) { mFontFeatureSettings = fontFeatureSettings; } @@ -82,6 +90,7 @@ public: private: float mLetterSpacing = 0; + float mWordSpacing = 0; std::string mFontFeatureSettings; uint32_t mMinikinLangListId; minikin::FontVariant mFontVariant; diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp index 84122d768089..67427433bb89 100644 --- a/libs/hwui/hwui/PaintImpl.cpp +++ b/libs/hwui/hwui/PaintImpl.cpp @@ -19,18 +19,19 @@ namespace android { Paint::Paint() : - SkPaint(), mLetterSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0), - mFontVariant(minikin::VARIANT_DEFAULT) { + SkPaint(), mLetterSpacing(0), mWordSpacing(0), mFontFeatureSettings(), + mMinikinLangListId(0), mFontVariant(minikin::VARIANT_DEFAULT) { } Paint::Paint(const Paint& paint) : SkPaint(paint), - mLetterSpacing(paint.mLetterSpacing), mFontFeatureSettings(paint.mFontFeatureSettings), + mLetterSpacing(paint.mLetterSpacing), mWordSpacing(paint.mWordSpacing), + mFontFeatureSettings(paint.mFontFeatureSettings), mMinikinLangListId(paint.mMinikinLangListId), mFontVariant(paint.mFontVariant), mHyphenEdit(paint.mHyphenEdit) { } Paint::Paint(const SkPaint& paint) : SkPaint(paint), - mLetterSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0), + mLetterSpacing(0), mWordSpacing(0), mFontFeatureSettings(), mMinikinLangListId(0), mFontVariant(minikin::VARIANT_DEFAULT) { } @@ -40,6 +41,7 @@ Paint::~Paint() { Paint& Paint::operator=(const Paint& other) { SkPaint::operator=(other); mLetterSpacing = other.mLetterSpacing; + mWordSpacing = other.mWordSpacing; mFontFeatureSettings = other.mFontFeatureSettings; mMinikinLangListId = other.mMinikinLangListId; mFontVariant = other.mFontVariant; @@ -50,6 +52,7 @@ Paint& Paint::operator=(const Paint& other) { bool operator==(const Paint& a, const Paint& b) { return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) && a.mLetterSpacing == b.mLetterSpacing + && a.mWordSpacing == b.mWordSpacing && a.mFontFeatureSettings == b.mFontFeatureSettings && a.mMinikinLangListId == b.mMinikinLangListId && a.mFontVariant == b.mFontVariant diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp index 13a0ed852816..f2af4a891b12 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.cpp +++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp @@ -23,36 +23,41 @@ namespace uirenderer { namespace skiapipeline { void LayerDrawable::onDraw(SkCanvas* canvas) { + DrawLayer(canvas->getGrContext(), canvas, mLayer.get()); +} + +bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) { // transform the matrix based on the layer int saveCount = -1; - if (!mLayer->getTransform().isIdentity()) { + if (!layer->getTransform().isIdentity()) { saveCount = canvas->save(); SkMatrix transform; - mLayer->getTransform().copyTo(transform); + layer->getTransform().copyTo(transform); canvas->concat(transform); } GrGLTextureInfo externalTexture; - externalTexture.fTarget = mLayer->getRenderTarget(); - externalTexture.fID = mLayer->getTextureId(); - GrContext* context = canvas->getGrContext(); + externalTexture.fTarget = layer->getRenderTarget(); + externalTexture.fID = layer->getTextureId(); GrBackendTextureDesc textureDescription; - textureDescription.fWidth = mLayer->getWidth(); - textureDescription.fHeight = mLayer->getHeight(); + textureDescription.fWidth = layer->getWidth(); + textureDescription.fHeight = layer->getHeight(); textureDescription.fConfig = kRGBA_8888_GrPixelConfig; textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin; textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture); sk_sp<SkImage> layerImage = SkImage::MakeFromTexture(context, textureDescription); if (layerImage) { SkPaint paint; - paint.setAlpha(mLayer->getAlpha()); - paint.setBlendMode(mLayer->getMode()); - paint.setColorFilter(sk_ref_sp(mLayer->getColorFilter())); + paint.setAlpha(layer->getAlpha()); + paint.setBlendMode(layer->getMode()); + paint.setColorFilter(sk_ref_sp(layer->getColorFilter())); canvas->drawImage(layerImage, 0, 0, &paint); } // restore the original matrix if (saveCount >= 0) { canvas->restoreToCount(saveCount); } + + return layerImage; } }; // namespace skiapipeline diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h index 91e274475b34..431989519a70 100644 --- a/libs/hwui/pipeline/skia/LayerDrawable.h +++ b/libs/hwui/pipeline/skia/LayerDrawable.h @@ -33,6 +33,7 @@ class LayerDrawable : public SkDrawable { explicit LayerDrawable(Layer* layer) : mLayer(layer) {} + static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer); protected: virtual SkRect onGetBounds() override { return SkRect::MakeWH(mLayer->getWidth(), mLayer->getHeight()); diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index f046e4b93db1..7f3474a1bdf3 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -17,9 +17,9 @@ #include "SkiaOpenGLPipeline.h" #include "DeferredLayerUpdater.h" +#include "LayerDrawable.h" #include "renderthread/EglManager.h" #include "renderstate/RenderState.h" -#include "Readback.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" #include "utils/TraceUtils.h" @@ -121,10 +121,16 @@ bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew, return *requireSwap; } -bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { - layer->apply(); - return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap) - == CopyResult::Success; +bool SkiaOpenGLPipeline::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) { + if (!mRenderThread.getGrContext()) { + return false; + } + + deferredLayer->apply(); + + SkCanvas canvas(*bitmap); + Layer* layer = deferredLayer->backingLayer(); + return LayerDrawable::DrawLayer(mRenderThread.getGrContext(), &canvas, layer); } DeferredLayerUpdater* SkiaOpenGLPipeline::createTextureLayer() { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp new file mode 100644 index 000000000000..a18d26471a29 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SkiaOpenGLReadback.h" + +#include "Matrix.h" +#include "Properties.h" +#include <SkCanvas.h> +#include <SkSurface.h> +#include <gl/GrGLInterface.h> +#include <gl/GrGLTypes.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +using namespace android::uirenderer::renderthread; + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +CopyResult SkiaOpenGLReadback::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, + int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) { + + GLuint sourceTexId; + glGenTextures(1, &sourceTexId); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId); + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage); + + sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext()); + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + sk_sp<const GrGLInterface> glInterface(GrGLCreateNativeInterface()); + LOG_ALWAYS_FATAL_IF(!glInterface.get()); + grContext.reset(GrContext::Create(GrBackend::kOpenGL_GrBackend, + (GrBackendContext)glInterface.get())); + } else { + grContext->resetContext(); + } + + GrGLTextureInfo externalTexture; + externalTexture.fTarget = GL_TEXTURE_EXTERNAL_OES; + externalTexture.fID = sourceTexId; + + GrBackendTextureDesc textureDescription; + textureDescription.fWidth = imgWidth; + textureDescription.fHeight = imgHeight; + textureDescription.fConfig = kRGBA_8888_GrPixelConfig; + textureDescription.fOrigin = kTopLeft_GrSurfaceOrigin; + textureDescription.fTextureHandle = reinterpret_cast<GrBackendObject>(&externalTexture); + + CopyResult copyResult = CopyResult::UnknownError; + sk_sp<SkImage> image(SkImage::MakeFromAdoptedTexture(grContext.get(), textureDescription)); + if (image) { + SkAutoLockPixels alp(*bitmap); + + // convert to Skia data structures + const SkRect bufferRect = SkRect::MakeIWH(imgWidth, imgHeight); + SkRect skiaSrcRect = srcRect.toSkRect(); + SkMatrix textureMatrix; + imgTransform.copyTo(textureMatrix); + + // remove the y-flip applied to the matrix so that we can scale the srcRect. + // This flip is not needed as we specify the origin of the texture when we + // wrap it as an SkImage. + SkMatrix yFlip = SkMatrix::MakeScale(1, -1); + yFlip.postTranslate(0,1); + textureMatrix.preConcat(yFlip); + + // copy the entire src if the rect is empty + if (skiaSrcRect.isEmpty()) { + skiaSrcRect = bufferRect; + } + + // since the y-flip has been removed we can simply scale & translate + // the source rectangle + textureMatrix.mapRect(&skiaSrcRect); + + if (skiaSrcRect.intersect(bufferRect)) { + SkPoint srcOrigin = SkPoint::Make(skiaSrcRect.fLeft, skiaSrcRect.fTop); + + // if we need to scale the result we must render to an offscreen buffer + if (bitmap->width() != skiaSrcRect.width() + || bitmap->height() != skiaSrcRect.height()) { + sk_sp<SkSurface> scaledSurface = SkSurface::MakeRenderTarget( + grContext.get(), SkBudgeted::kYes, bitmap->info()); + SkPaint paint; + paint.setBlendMode(SkBlendMode::kSrc); + scaledSurface->getCanvas()->drawImageRect(image, skiaSrcRect, + SkRect::MakeWH(bitmap->width(), bitmap->height()), &paint); + image = scaledSurface->makeImageSnapshot(); + srcOrigin.set(0,0); + } + + if (image->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), + srcOrigin.fX, srcOrigin.fY)) { + copyResult = CopyResult::Success; + } + } + } + + // make sure that we have deleted the texture (in the SkImage) before we + // destroy the EGLImage that it was created from + image.reset(); + return copyResult; +} + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h new file mode 100644 index 000000000000..d914409628d0 --- /dev/null +++ b/libs/hwui/pipeline/skia/SkiaOpenGLReadback.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "OpenGLReadback.h" + +namespace android { +namespace uirenderer { +namespace skiapipeline { + +class SkiaOpenGLReadback : public OpenGLReadback { +public: + SkiaOpenGLReadback(renderthread::RenderThread& thread) : OpenGLReadback(thread) {} +protected: + virtual CopyResult copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, + int imgWidth, int imgHeight, const Rect& srcRect, SkBitmap* bitmap) override; +}; + +} /* namespace skiapipeline */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp index afeeef86d22c..177a729cbf55 100644 --- a/libs/hwui/renderthread/OpenGLPipeline.cpp +++ b/libs/hwui/renderthread/OpenGLPipeline.cpp @@ -20,7 +20,7 @@ #include "EglManager.h" #include "ProfileRenderer.h" #include "renderstate/RenderState.h" -#include "Readback.h" +#include "OpenGLReadback.h" #include <android/native_window.h> #include <cutils/properties.h> @@ -117,9 +117,9 @@ bool OpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& sc } bool OpenGLPipeline::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { + ATRACE_CALL(); layer->apply(); - return Readback::copyTextureLayerInto(mRenderThread, *(layer->backingLayer()), bitmap) - == CopyResult::Success; + return OpenGLReadbackImpl::copyLayerInto(mRenderThread, *(layer->backingLayer()), bitmap); } DeferredLayerUpdater* OpenGLPipeline::createTextureLayer() { diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 39e5931da361..2c4824279325 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -602,8 +602,8 @@ void RenderProxy::removeFrameMetricsObserver(FrameMetricsObserver* observer) { CREATE_BRIDGE4(copySurfaceInto, RenderThread* thread, Surface* surface, Rect srcRect, SkBitmap* bitmap) { - return (void*) Readback::copySurfaceInto(*args->thread, - *args->surface, args->srcRect, args->bitmap); + return (void*)args->thread->readback().copySurfaceInto(*args->surface, + args->srcRect, args->bitmap); } int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index f3789c8d8cbb..223958a3c319 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -17,8 +17,10 @@ #include "RenderThread.h" #include "../renderstate/RenderState.h" +#include "../pipeline/skia/SkiaOpenGLReadback.h" #include "CanvasContext.h" #include "EglManager.h" +#include "OpenGLReadback.h" #include "RenderProxy.h" #include "VulkanManager.h" @@ -196,6 +198,30 @@ void RenderThread::initThreadLocals() { mVkManager = new VulkanManager(*this); } +Readback& RenderThread::readback() { + + if (!mReadback) { + auto renderType = Properties::getRenderPipelineType(); + switch (renderType) { + case RenderPipelineType::OpenGL: + mReadback = new OpenGLReadbackImpl(*this); + break; + case RenderPipelineType::SkiaGL: + case RenderPipelineType::SkiaVulkan: + // It works to use the OpenGL pipeline for Vulkan but this is not + // ideal as it causes us to create an OpenGL context in addition + // to the Vulkan one. + mReadback = new skiapipeline::SkiaOpenGLReadback(*this); + break; + default: + LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType); + break; + } + } + + return *mReadback; +} + int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { ALOGE("Display event receiver pipe was closed or an error occurred. " diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 12050dd9c772..d121bcf5b084 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -37,6 +37,7 @@ class DisplayEventReceiver; namespace uirenderer { +class Readback; class RenderState; class TestUtils; @@ -93,6 +94,7 @@ public: RenderState& renderState() const { return *mRenderState; } EglManager& eglManager() const { return *mEglManager; } JankTracker& jankTracker() { return *mJankTracker; } + Readback& readback(); const DisplayInfo& mainDisplayInfo() { return mDisplayInfo; } @@ -151,6 +153,7 @@ private: EglManager* mEglManager; JankTracker* mJankTracker = nullptr; + Readback* mReadback = nullptr; sk_sp<GrContext> mGrContext; VulkanManager* mVkManager; diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index d77a082188f6..bbb7184664b6 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -2189,9 +2189,9 @@ public class ExifInterface { /** * Loads EXIF attributes from a JPEG input stream. * - * @param inputStream The input stream that starts with the JPEG data. + * @param in The input stream that starts with the JPEG data. * @param jpegOffset The offset value in input stream for JPEG data. - * @param imageTypes The image type from which to retrieve metadata. Use IFD_TYPE_PRIMARY for + * @param imageType The image type from which to retrieve metadata. Use IFD_TYPE_PRIMARY for * primary image, IFD_TYPE_PREVIEW for preview image, and * IFD_TYPE_THUMBNAIL for thumbnail image. * @throws IOException If the data contains invalid JPEG markers, offsets, or length values. diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java index e07856e46779..7e3f67b875e5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java +++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java @@ -39,6 +39,8 @@ public final class CategoryKey { public static final String CATEGORY_SYSTEM_INPUT = "com.android.settings.category.ia.input"; public static final String CATEGORY_SYSTEM_LANGUAGE = "com.android.settings.category.ia.language"; + public static final String CATEGORY_SYSTEM_DEVELOPMENT = + "com.android.settings.category.ia.development"; public static final Map<String, String> KEY_COMPAT_MAP; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java index a0254cd94923..b209f4ec665a 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryKeyTest.java @@ -58,9 +58,10 @@ public class CategoryKeyTest { allKeys.add(CategoryKey.CATEGORY_SYSTEM); allKeys.add(CategoryKey.CATEGORY_SYSTEM_INPUT); allKeys.add(CategoryKey.CATEGORY_SYSTEM_LANGUAGE); + allKeys.add(CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT); // DO NOT REMOVE ANYTHING ABOVE - assertThat(allKeys.size()).isEqualTo(13); + assertThat(allKeys.size()).isEqualTo(14); } } diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 9b4b1868cdbb..45f2ec76588a 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -2653,6 +2653,12 @@ message MetricsEvent { // OS: O ENTERPRISE_PRIVACY_SETTINGS = 628; + // ACTION: Longpress on a TextView. + // SUBTYPE: 1 is for START_SELECTION, 2 is for START_DRAG_AND_DROP, 0 is for OTHER. + // CATEGORY: TEXT_CONTROLS + // OS: O + TEXT_LONGPRESS = 629; + // ---- End O Constants, all O constants go above this line ---- // Add new aosp constants above this line. diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 543bba660ecc..883d2d81e19c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8699,9 +8699,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Managed user cannot have a managed profile. return false; } + boolean canRemoveProfile + = !mUserManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER); final long ident = mInjector.binderClearCallingIdentity(); try { - if (!mUserManager.canAddMoreManagedProfiles(callingUserId, true)) { + if (!mUserManager.canAddMoreManagedProfiles(callingUserId, canRemoveProfile)) { return false; } } finally { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 1202025751c7..ba23f219c106 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -52,6 +52,7 @@ import android.view.WindowManager; import com.android.internal.R; import com.android.internal.app.NightDisplayController; +import com.android.internal.logging.MetricsLogger; import com.android.internal.os.BinderInternal; import com.android.internal.os.SamplingProfilerIntegration; import com.android.internal.os.ZygoteInit; @@ -275,7 +276,9 @@ public final class SystemServer { // Here we go! Slog.i(TAG, "Entered the Android system server!"); - EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, SystemClock.uptimeMillis()); + int uptimeMillis = (int) SystemClock.uptimeMillis(); + EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, uptimeMillis); + MetricsLogger.histogram(null, "boot_system_server_init", uptimeMillis); // In case the runtime switched since last boot (such as when // the old runtime was removed in an OTA), set the system @@ -364,6 +367,7 @@ public final class SystemServer { if (StrictMode.conditionallyEnableDebugLogging()) { Slog.i(TAG, "Enabled StrictMode for system server main thread."); } + MetricsLogger.histogram(null, "boot_system_server_ready", (int) SystemClock.uptimeMillis()); // Loop forever. Looper.loop(); @@ -492,13 +496,16 @@ public final class SystemServer { } // Start the package manager. + MetricsLogger.histogram(null, "boot_package_manager_init_start", + (int) SystemClock.uptimeMillis()); traceBeginAndSlog("StartPackageManagerService"); mPackageManagerService = PackageManagerService.main(mSystemContext, installer, mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore); mFirstBoot = mPackageManagerService.isFirstBoot(); mPackageManager = mSystemContext.getPackageManager(); traceEnd(); - + MetricsLogger.histogram(null, "boot_package_manager_init_ready", + (int) SystemClock.uptimeMillis()); // Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename // A/B artifacts after boot, before anything else might touch/need them. // Note: this isn't needed during decryption (we don't have /data anyways). diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 56ff6214a908..71379b8258e2 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -2178,6 +2178,26 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true); } + public void testIsProvisioningAllowed_provisionManagedProfileCantRemoveUser_primaryUser() + throws Exception { + setDeviceOwner(); + + when(mContext.ipackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS, 0)) + .thenReturn(true); + when(mContext.userManagerForMock.isSplitSystemUser()).thenReturn(true); + when(mContext.userManager.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER)) + .thenReturn(true); + when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE, + false /* we can't remove a managed profile*/)).thenReturn(false); + when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE, + true)).thenReturn(true); + setUserSetupCompleteForUser(false, DpmMockContext.CALLER_USER_HANDLE); + + mContext.binder.callingUid = DpmMockContext.CALLER_UID; + + assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false); + } + public void testForceUpdateUserSetupComplete_permission() { // GIVEN the permission MANAGE_PROFILE_AND_DEVICE_OWNERS is not granted try { |