diff options
Diffstat (limited to 'native')
-rw-r--r-- | native/android/Android.bp | 24 | ||||
-rw-r--r-- | native/android/choreographer.cpp | 225 | ||||
-rw-r--r-- | native/android/libandroid.map.txt | 8 | ||||
-rw-r--r-- | native/android/surface_control.cpp | 30 | ||||
-rw-r--r-- | native/android/surface_texture.cpp | 76 | ||||
-rw-r--r-- | native/android/system_fonts.cpp | 61 | ||||
-rw-r--r-- | native/android/thermal.cpp | 261 | ||||
-rw-r--r-- | native/graphics/jni/Android.bp | 13 | ||||
-rw-r--r-- | native/graphics/jni/aassetstreamadaptor.cpp | 117 | ||||
-rw-r--r-- | native/graphics/jni/aassetstreamadaptor.h | 50 | ||||
-rw-r--r-- | native/graphics/jni/bitmap.cpp | 65 | ||||
-rw-r--r-- | native/graphics/jni/imagedecoder.cpp | 348 | ||||
-rw-r--r-- | native/graphics/jni/libjnigraphics.map.txt | 22 | ||||
-rw-r--r-- | native/webview/plat_support/Android.bp | 4 | ||||
-rw-r--r-- | native/webview/plat_support/graphics_utils.cpp | 4 |
15 files changed, 958 insertions, 350 deletions
diff --git a/native/android/Android.bp b/native/android/Android.bp index ae8cb3a47a05..ed73f39e57f8 100644 --- a/native/android/Android.bp +++ b/native/android/Android.bp @@ -25,6 +25,7 @@ cc_defaults { cflags: [ "-Wall", "-Werror", + "-Wextra", "-Wunused", "-Wunreachable-code", ], @@ -36,7 +37,6 @@ cc_library_shared { srcs: [ "asset_manager.cpp", - "choreographer.cpp", "configuration.cpp", "hardware_buffer_jni.cpp", "input.cpp", @@ -48,10 +48,10 @@ cc_library_shared { "sensor.cpp", "sharedmem.cpp", "storage_manager.cpp", - "surface_texture.cpp", "surface_control.cpp", "system_fonts.cpp", "trace.cpp", + "thermal.cpp" ], shared_libs: [ @@ -71,6 +71,9 @@ cc_library_shared { "libnetd_client", "libhwui", "libxml2", + "libEGL", + "libGLESv2", + "libpowermanager", "android.hardware.configstore@1.0", "android.hardware.configstore-utils", ], @@ -80,7 +83,9 @@ cc_library_shared { "libarect", ], - whole_static_libs: ["libnativewindow"], + header_libs: [ "libhwui_internal_headers" ], + + whole_static_libs: ["libnativedisplay", "libnativewindow"], export_static_lib_headers: ["libarect"], @@ -115,11 +120,16 @@ llndk_library { // Aidl library for platform compat. cc_library_shared { name: "lib-platform-compat-native-api", - defaults: ["libandroid_defaults"], - + cflags: [ + "-Wall", + "-Werror", + "-Wno-missing-field-initializers", + "-Wno-unused-variable", + "-Wunused-parameter", + ], shared_libs: [ "libbinder", - "libutils", + "libutils", ], aidl: { local_include_dirs: ["aidl"], @@ -137,4 +147,4 @@ filegroup { "aidl/com/android/internal/compat/IPlatformCompatNative.aidl", ], path: "aidl", -}
\ No newline at end of file +} diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp deleted file mode 100644 index 63e073405fe0..000000000000 --- a/native/android/choreographer.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2015 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. - */ - -#define LOG_TAG "Choreographer" -//#define LOG_NDEBUG 0 - -#include <cinttypes> -#include <queue> -#include <thread> - -#include <android/choreographer.h> -#include <androidfw/DisplayEventDispatcher.h> -#include <gui/ISurfaceComposer.h> -#include <gui/SurfaceComposerClient.h> -#include <utils/Looper.h> -#include <utils/Mutex.h> -#include <utils/Timers.h> - -namespace android { - -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - -struct FrameCallback { - AChoreographer_frameCallback callback; - AChoreographer_frameCallback64 callback64; - void* data; - nsecs_t dueTime; - - inline bool operator<(const FrameCallback& rhs) const { - // Note that this is intentionally flipped because we want callbacks due sooner to be at - // the head of the queue - return dueTime > rhs.dueTime; - } -}; - - -class Choreographer : public DisplayEventDispatcher, public MessageHandler { -public: - void postFrameCallbackDelayed(AChoreographer_frameCallback cb, - AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay); - - enum { - MSG_SCHEDULE_CALLBACKS = 0, - MSG_SCHEDULE_VSYNC = 1 - }; - virtual void handleMessage(const Message& message) override; - - static Choreographer* getForThread(); - -protected: - virtual ~Choreographer() = default; - -private: - explicit Choreographer(const sp<Looper>& looper); - Choreographer(const Choreographer&) = delete; - - void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override; - void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; - void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, - int32_t configId) override; - - void scheduleCallbacks(); - - // Protected by mLock - std::priority_queue<FrameCallback> mCallbacks; - - mutable Mutex mLock; - - const sp<Looper> mLooper; - const std::thread::id mThreadId; -}; - - -static thread_local Choreographer* gChoreographer; -Choreographer* Choreographer::getForThread() { - if (gChoreographer == nullptr) { - sp<Looper> looper = Looper::getForThread(); - if (!looper.get()) { - ALOGW("No looper prepared for thread"); - return nullptr; - } - gChoreographer = new Choreographer(looper); - status_t result = gChoreographer->initialize(); - if (result != OK) { - ALOGW("Failed to initialize"); - return nullptr; - } - } - return gChoreographer; -} - -Choreographer::Choreographer(const sp<Looper>& looper) : - DisplayEventDispatcher(looper), mLooper(looper), mThreadId(std::this_thread::get_id()) { -} - -void Choreographer::postFrameCallbackDelayed( - AChoreographer_frameCallback cb, AChoreographer_frameCallback64 cb64, void* data, nsecs_t delay) { - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - FrameCallback callback{cb, cb64, data, now + delay}; - { - AutoMutex _l{mLock}; - mCallbacks.push(callback); - } - if (callback.dueTime <= now) { - if (std::this_thread::get_id() != mThreadId) { - Message m{MSG_SCHEDULE_VSYNC}; - mLooper->sendMessage(this, m); - } else { - scheduleVsync(); - } - } else { - Message m{MSG_SCHEDULE_CALLBACKS}; - mLooper->sendMessageDelayed(delay, this, m); - } -} - -void Choreographer::scheduleCallbacks() { - AutoMutex _{mLock}; - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - if (mCallbacks.top().dueTime <= now) { - ALOGV("choreographer %p ~ scheduling vsync", this); - scheduleVsync(); - return; - } -} - -// TODO(b/74619554): The PhysicalDisplayId is ignored because SF only emits VSYNC events for the -// internal display and DisplayEventReceiver::requestNextVsync only allows requesting VSYNC for -// the internal display implicitly. -void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t) { - std::vector<FrameCallback> callbacks{}; - { - AutoMutex _l{mLock}; - nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); - while (!mCallbacks.empty() && mCallbacks.top().dueTime < now) { - callbacks.push_back(mCallbacks.top()); - mCallbacks.pop(); - } - } - for (const auto& cb : callbacks) { - if (cb.callback64 != nullptr) { - cb.callback64(timestamp, cb.data); - } else if (cb.callback != nullptr) { - cb.callback(timestamp, cb.data); - } - } -} - -void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) { - ALOGV("choreographer %p ~ received hotplug event (displayId=%" - ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", connected=%s), ignoring.", - this, displayId, toString(connected)); -} - -void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, - int32_t configId) { - ALOGV("choreographer %p ~ received config changed event (displayId=%" - ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.", - this, displayId, toString(configId)); -} - -void Choreographer::handleMessage(const Message& message) { - switch (message.what) { - case MSG_SCHEDULE_CALLBACKS: - scheduleCallbacks(); - break; - case MSG_SCHEDULE_VSYNC: - scheduleVsync(); - break; - } -} - -} - -/* Glue for the NDK interface */ - -using android::Choreographer; - -static inline Choreographer* AChoreographer_to_Choreographer(AChoreographer* choreographer) { - return reinterpret_cast<Choreographer*>(choreographer); -} - -static inline AChoreographer* Choreographer_to_AChoreographer(Choreographer* choreographer) { - return reinterpret_cast<AChoreographer*>(choreographer); -} - -AChoreographer* AChoreographer_getInstance() { - return Choreographer_to_AChoreographer(Choreographer::getForThread()); -} - -void AChoreographer_postFrameCallback(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data) { - AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( - callback, nullptr, data, 0); -} -void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, - AChoreographer_frameCallback callback, void* data, long delayMillis) { - AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( - callback, nullptr, data, ms2ns(delayMillis)); -} -void AChoreographer_postFrameCallback64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data) { - AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( - nullptr, callback, data, 0); -} -void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, - AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) { - AChoreographer_to_Choreographer(choreographer)->postFrameCallbackDelayed( - nullptr, callback, data, ms2ns(delayMillis)); -} diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 203adfc749d2..d56aa86ae6fa 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -23,6 +23,8 @@ LIBANDROID { AChoreographer_postFrameCallbackDelayed; # introduced=24 AChoreographer_postFrameCallback64; # introduced=29 AChoreographer_postFrameCallbackDelayed64; # introduced=29 + AChoreographer_registerRefreshRateCallback; # introduced=30 + AChoreographer_unregisterRefreshRateCallback; # introduced=30 AConfiguration_copy; AConfiguration_delete; AConfiguration_diff; @@ -240,6 +242,7 @@ LIBANDROID { ASurfaceTransaction_setColor; # introduced=29 ASurfaceTransaction_setDamageRegion; # introduced=29 ASurfaceTransaction_setDesiredPresentTime; # introduced=29 + ASurfaceTransaction_setFrameRate; # introduced=30 ASurfaceTransaction_setGeometry; # introduced=29 ASurfaceTransaction_setHdrMetadata_cta861_3; # introduced=29 ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29 @@ -277,6 +280,11 @@ LIBANDROID { android_res_nquery; # introduced=29 android_res_nresult; # introduced=29 android_res_nsend; # introduced=29 + AThermal_acquireManager; # introduced=30 + AThermal_releaseManager; # introduced=30 + AThermal_getCurrentThermalStatus; # introduced=30 + AThermal_registerThermalStatusListener; # introduced=30 + AThermal_unregisterThermalStatusListener; # introduced=30 local: *; }; diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index 8fe4fecceeb5..0af6cbf3cb40 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -100,7 +100,11 @@ static bool isDataSpaceValid(const sp<SurfaceControl>& surfaceControl, ADataSpac return getWideColorSupport(surfaceControl); // These data space need HDR support. case HAL_DATASPACE_BT2020_PQ: - return getHdrSupport(surfaceControl); + if (!getHdrSupport(surfaceControl)) { + ALOGE("Invalid dataspace - device does not support hdr"); + return false; + } + return true; default: return false; } @@ -290,7 +294,7 @@ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* aSurfaceTransaction, auto& aSurfaceControlStats = aSurfaceTransactionStats.aSurfaceControlStats; - for (const auto& [surfaceControl, acquireTime, previousReleaseFence] : surfaceControlStats) { + for (const auto& [surfaceControl, latchTime, acquireTime, presentFence, previousReleaseFence, transformHint, frameEvents] : surfaceControlStats) { ASurfaceControl* aSurfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControl.get()); aSurfaceControlStats[aSurfaceControl].acquireTime = acquireTime; aSurfaceControlStats[aSurfaceControl].previousReleaseFence = previousReleaseFence; @@ -458,10 +462,11 @@ void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* aSurfaceTransac CHECK_NOT_NULL(aSurfaceControl); sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); - LOG_ALWAYS_FATAL_IF(!isDataSpaceValid(surfaceControl, aDataSpace), "invalid dataspace"); - + if (!isDataSpaceValid(surfaceControl, aDataSpace)) { + ALOGE("Failed to set buffer dataspace - invalid dataspace"); + return; + } Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); - transaction->setDataspace(surfaceControl, static_cast<ui::Dataspace>(aDataSpace)); } @@ -527,7 +532,10 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction, CHECK_NOT_NULL(aSurfaceControl); sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); - LOG_ALWAYS_FATAL_IF(!isDataSpaceValid(surfaceControl, dataspace), "invalid dataspace"); + if (!isDataSpaceValid(surfaceControl, dataspace)) { + ALOGE("Failed to set buffer dataspace - invalid dataspace"); + return; + } Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); half3 color; @@ -537,3 +545,13 @@ void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction, transaction->setBackgroundColor(surfaceControl, color, alpha, static_cast<ui::Dataspace>(dataspace)); } + +void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* aSurfaceTransaction, + ASurfaceControl* aSurfaceControl, float frameRate, + int8_t compatibility) { + CHECK_NOT_NULL(aSurfaceTransaction); + CHECK_NOT_NULL(aSurfaceControl); + Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction); + sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl); + transaction->setFrameRate(surfaceControl, frameRate, compatibility); +} diff --git a/native/android/surface_texture.cpp b/native/android/surface_texture.cpp deleted file mode 100644 index ced2792775d4..000000000000 --- a/native/android/surface_texture.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <android/surface_texture.h> -#include <android/surface_texture_jni.h> - -#define LOG_TAG "ASurfaceTexture" - -#include <utils/Log.h> - -#include <gui/Surface.h> - -#include <android_runtime/android_graphics_SurfaceTexture.h> - -#include "surfacetexture/SurfaceTexture.h" - -using namespace android; - -struct ASurfaceTexture { - sp<SurfaceTexture> consumer; - sp<IGraphicBufferProducer> producer; -}; - -ASurfaceTexture* ASurfaceTexture_fromSurfaceTexture(JNIEnv* env, jobject surfacetexture) { - if (!surfacetexture || !android_SurfaceTexture_isInstanceOf(env, surfacetexture)) { - return nullptr; - } - ASurfaceTexture* ast = new ASurfaceTexture; - ast->consumer = SurfaceTexture_getSurfaceTexture(env, surfacetexture); - ast->producer = SurfaceTexture_getProducer(env, surfacetexture); - return ast; -} - -ANativeWindow* ASurfaceTexture_acquireANativeWindow(ASurfaceTexture* st) { - sp<Surface> surface = new Surface(st->producer); - ANativeWindow* win(surface.get()); - ANativeWindow_acquire(win); - return win; -} - -void ASurfaceTexture_release(ASurfaceTexture* st) { - delete st; -} - -int ASurfaceTexture_attachToGLContext(ASurfaceTexture* st, uint32_t tex) { - return st->consumer->attachToContext(tex); -} - -int ASurfaceTexture_detachFromGLContext(ASurfaceTexture* st) { - return st->consumer->detachFromContext(); -} - -int ASurfaceTexture_updateTexImage(ASurfaceTexture* st) { - return st->consumer->updateTexImage(); -} - -void ASurfaceTexture_getTransformMatrix(ASurfaceTexture* st, float mtx[16]) { - st->consumer->getTransformMatrix(mtx); -} - -int64_t ASurfaceTexture_getTimestamp(ASurfaceTexture* st) { - return st->consumer->getTimestamp(); -} diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp index 9791da63359b..45f42f1b5dc6 100644 --- a/native/android/system_fonts.cpp +++ b/native/android/system_fonts.cpp @@ -16,6 +16,8 @@ #include <jni.h> +#define LOG_TAG "SystemFont" + #include <android/font.h> #include <android/font_matcher.h> #include <android/system_fonts.h> @@ -47,9 +49,14 @@ struct XmlDocDeleter { using XmlCharUniquePtr = std::unique_ptr<xmlChar, XmlCharDeleter>; using XmlDocUniquePtr = std::unique_ptr<xmlDoc, XmlDocDeleter>; +struct ParserState { + xmlNode* mFontNode = nullptr; + XmlCharUniquePtr mLocale; +}; + struct ASystemFontIterator { XmlDocUniquePtr mXmlDoc; - xmlNode* mFontNode; + ParserState state; // The OEM customization XML. XmlDocUniquePtr mCustomizationXmlDoc; @@ -97,6 +104,7 @@ std::string xmlTrim(const std::string& in) { const xmlChar* FAMILY_TAG = BAD_CAST("family"); const xmlChar* FONT_TAG = BAD_CAST("font"); +const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang"); xmlNode* firstElement(xmlNode* node, const xmlChar* tag) { for (xmlNode* child = node->children; child; child = child->next) { @@ -116,9 +124,9 @@ xmlNode* nextSibling(xmlNode* node, const xmlChar* tag) { return nullptr; } -void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, AFont* out, +void copyFont(const XmlDocUniquePtr& xmlDoc, const ParserState& state, AFont* out, const std::string& pathPrefix) { - const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang"); + xmlNode* fontNode = state.mFontNode; XmlCharUniquePtr filePathStr( xmlNodeListGetString(xmlDoc.get(), fontNode->xmlChildrenNode, 1)); out->mFilePath = pathPrefix + xmlTrim( @@ -139,9 +147,10 @@ void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, AFont* out, out->mCollectionIndex = indexStr ? strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0; - XmlCharUniquePtr localeStr(xmlGetProp(xmlDoc->parent, LOCALE_ATTR_NAME)); out->mLocale.reset( - localeStr ? new std::string(reinterpret_cast<const char*>(localeStr.get())) : nullptr); + state.mLocale ? + new std::string(reinterpret_cast<const char*>(state.mLocale.get())) + : nullptr); const xmlChar* TAG_ATTR_NAME = BAD_CAST("tag"); const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue"); @@ -178,25 +187,27 @@ bool isFontFileAvailable(const std::string& filePath) { return S_ISREG(st.st_mode); } -xmlNode* findFirstFontNode(const XmlDocUniquePtr& doc) { +bool findFirstFontNode(const XmlDocUniquePtr& doc, ParserState* state) { xmlNode* familySet = xmlDocGetRootElement(doc.get()); if (familySet == nullptr) { - return nullptr; + return false; } xmlNode* family = firstElement(familySet, FAMILY_TAG); if (family == nullptr) { - return nullptr; + return false; } + state->mLocale.reset(xmlGetProp(family, LOCALE_ATTR_NAME)); xmlNode* font = firstElement(family, FONT_TAG); while (font == nullptr) { family = nextSibling(family, FAMILY_TAG); if (family == nullptr) { - return nullptr; + return false; } font = firstElement(family, FONT_TAG); } - return font; + state->mFontNode = font; + return font != nullptr; } } // namespace @@ -272,38 +283,38 @@ AFont* _Nonnull AFontMatcher_match( return result.release(); } -xmlNode* findNextFontNode(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode) { - if (fontNode == nullptr) { +bool findNextFontNode(const XmlDocUniquePtr& xmlDoc, ParserState* state) { + if (state->mFontNode == nullptr) { if (!xmlDoc) { - return nullptr; // Already at the end. + return false; // Already at the end. } else { // First time to query font. - return findFirstFontNode(xmlDoc); + return findFirstFontNode(xmlDoc, state); } } else { - xmlNode* nextNode = nextSibling(fontNode, FONT_TAG); + xmlNode* nextNode = nextSibling(state->mFontNode, FONT_TAG); while (nextNode == nullptr) { - xmlNode* family = nextSibling(fontNode->parent, FAMILY_TAG); + xmlNode* family = nextSibling(state->mFontNode->parent, FAMILY_TAG); if (family == nullptr) { break; } + state->mLocale.reset(xmlGetProp(family, LOCALE_ATTR_NAME)); nextNode = firstElement(family, FONT_TAG); } - return nextNode; + state->mFontNode = nextNode; + return nextNode != nullptr; } } AFont* ASystemFontIterator_next(ASystemFontIterator* ite) { LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument"); if (ite->mXmlDoc) { - ite->mFontNode = findNextFontNode(ite->mXmlDoc, ite->mFontNode); - if (ite->mFontNode == nullptr) { + if (!findNextFontNode(ite->mXmlDoc, &ite->state)) { // Reached end of the XML file. Continue OEM customization. ite->mXmlDoc.reset(); - ite->mFontNode = nullptr; } else { std::unique_ptr<AFont> font = std::make_unique<AFont>(); - copyFont(ite->mXmlDoc, ite->mFontNode, font.get(), "/system/fonts/"); + copyFont(ite->mXmlDoc, ite->state, font.get(), "/system/fonts/"); if (!isFontFileAvailable(font->mFilePath)) { return ASystemFontIterator_next(ite); } @@ -312,15 +323,13 @@ AFont* ASystemFontIterator_next(ASystemFontIterator* ite) { } if (ite->mCustomizationXmlDoc) { // TODO: Filter only customizationType="new-named-family" - ite->mFontNode = findNextFontNode(ite->mCustomizationXmlDoc, ite->mFontNode); - if (ite->mFontNode == nullptr) { + if (!findNextFontNode(ite->mCustomizationXmlDoc, &ite->state)) { // Reached end of the XML file. Finishing ite->mCustomizationXmlDoc.reset(); - ite->mFontNode = nullptr; return nullptr; } else { std::unique_ptr<AFont> font = std::make_unique<AFont>(); - copyFont(ite->mCustomizationXmlDoc, ite->mFontNode, font.get(), "/product/fonts/"); + copyFont(ite->mCustomizationXmlDoc, ite->state, font.get(), "/product/fonts/"); if (!isFontFileAvailable(font->mFilePath)) { return ASystemFontIterator_next(ite); } @@ -351,7 +360,7 @@ bool AFont_isItalic(const AFont* font) { const char* AFont_getLocale(const AFont* font) { LOG_ALWAYS_FATAL_IF(font == nullptr, "nullptr has passed to font argument"); - return font->mLocale ? nullptr : font->mLocale->c_str(); + return font->mLocale ? font->mLocale->c_str() : nullptr; } size_t AFont_getCollectionIndex(const AFont* font) { diff --git a/native/android/thermal.cpp b/native/android/thermal.cpp new file mode 100644 index 000000000000..545c423908a0 --- /dev/null +++ b/native/android/thermal.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2020 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. + */ + +#define LOG_TAG "thermal" + +#include <cerrno> +#include <thread> + +#include <android/thermal.h> +#include <android/os/BnThermalStatusListener.h> +#include <android/os/IThermalService.h> +#include <binder/IServiceManager.h> +#include <utils/Log.h> + +using android::sp; + +using namespace android; +using namespace android::os; + +struct ThermalServiceListener : public BnThermalStatusListener { + public: + virtual binder::Status onStatusChange(int32_t status) override; + ThermalServiceListener(AThermalManager *manager) {mMgr = manager;} + private: + AThermalManager *mMgr; +}; + +struct ListenerCallback { + AThermal_StatusCallback callback; + void* data; +}; + +struct AThermalManager { + public: + static AThermalManager* createAThermalManager(); + AThermalManager() = delete; + ~AThermalManager(); + status_t notifyStateChange(int32_t status); + status_t getCurrentThermalStatus(int32_t *status); + status_t addListener(AThermal_StatusCallback, void *data); + status_t removeListener(AThermal_StatusCallback, void *data); + private: + AThermalManager(sp<IThermalService> service); + sp<IThermalService> mThermalSvc; + sp<ThermalServiceListener> mServiceListener; + std::vector<ListenerCallback> mListeners; + std::mutex mMutex; +}; + +binder::Status ThermalServiceListener::onStatusChange(int32_t status) { + if (mMgr != nullptr) { + mMgr->notifyStateChange(status); + } + return binder::Status::ok(); +} + +AThermalManager* AThermalManager::createAThermalManager() { + sp<IBinder> binder = + defaultServiceManager()->checkService(String16("thermalservice")); + + if (binder == nullptr) { + ALOGE("%s: Thermal service is not ready ", __FUNCTION__); + return nullptr; + } + return new AThermalManager(interface_cast<IThermalService>(binder)); +} + +AThermalManager::AThermalManager(sp<IThermalService> service) + : mThermalSvc(service), + mServiceListener(nullptr) { +} + +AThermalManager::~AThermalManager() { + std::unique_lock<std::mutex> lock(mMutex); + + mListeners.clear(); + if (mServiceListener != nullptr) { + bool success = false; + mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success); + mServiceListener = nullptr; + } +} + +status_t AThermalManager::notifyStateChange(int32_t status) { + std::unique_lock<std::mutex> lock(mMutex); + AThermalStatus thermalStatus = static_cast<AThermalStatus>(status); + + for (auto listener : mListeners) { + listener.callback(listener.data, thermalStatus); + } + return OK; +} + +status_t AThermalManager::addListener(AThermal_StatusCallback callback, void *data) { + std::unique_lock<std::mutex> lock(mMutex); + + if (callback == nullptr) { + // Callback can not be nullptr + return EINVAL; + } + for (const auto& cb : mListeners) { + // Don't re-add callbacks. + if (callback == cb.callback && data == cb.data) { + return EINVAL; + } + } + mListeners.emplace_back(ListenerCallback{callback, data}); + + if (mServiceListener != nullptr) { + return OK; + } + bool success = false; + mServiceListener = new ThermalServiceListener(this); + if (mServiceListener == nullptr) { + return ENOMEM; + } + auto ret = mThermalSvc->registerThermalStatusListener(mServiceListener, &success); + if (!success || !ret.isOk()) { + ALOGE("Failed in registerThermalStatusListener %d", success); + if (ret.exceptionCode() == binder::Status::EX_SECURITY) { + return EPERM; + } + return EPIPE; + } + return OK; +} + +status_t AThermalManager::removeListener(AThermal_StatusCallback callback, void *data) { + std::unique_lock<std::mutex> lock(mMutex); + + auto it = std::remove_if(mListeners.begin(), + mListeners.end(), + [&](const ListenerCallback& cb) { + return callback == cb.callback && + data == cb.data; + }); + if (it == mListeners.end()) { + // If the listener and data pointer were not previously added. + return EINVAL; + } + mListeners.erase(it, mListeners.end()); + + if (!mListeners.empty()) { + return OK; + } + if (mServiceListener == nullptr) { + return OK; + } + bool success = false; + auto ret = mThermalSvc->unregisterThermalStatusListener(mServiceListener, &success); + if (!success || !ret.isOk()) { + ALOGE("Failed in unregisterThermalStatusListener %d", success); + if (ret.exceptionCode() == binder::Status::EX_SECURITY) { + return EPERM; + } + return EPIPE; + } + mServiceListener = nullptr; + return OK; +} + +status_t AThermalManager::getCurrentThermalStatus(int32_t *status) { + binder::Status ret = mThermalSvc->getCurrentThermalStatus(status); + + if (!ret.isOk()) { + if (ret.exceptionCode() == binder::Status::EX_SECURITY) { + return EPERM; + } + return EPIPE; + } + return OK; +} + +/** + * Acquire an instance of the thermal manager. This must be freed using + * {@link AThermal_releaseManager}. + * + * @return manager instance on success, nullptr on failure. + */ +AThermalManager* AThermal_acquireManager() { + auto manager = AThermalManager::createAThermalManager(); + + return manager; +} + +/** + * Release the thermal manager pointer acquired by + * {@link AThermal_acquireManager}. + * + * @param manager The manager to be released. + * + */ +void AThermal_releaseManager(AThermalManager *manager) { + delete manager; +} + +/** + * Gets the current thermal status. + * + * @param manager The manager instance to use to query the thermal status, + * acquired by {@link AThermal_acquireManager}. + * + * @return current thermal status, ATHERMAL_STATUS_ERROR on failure. +*/ +AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) { + int32_t status = 0; + status_t ret = manager->getCurrentThermalStatus(&status); + if (ret != OK) { + return AThermalStatus::ATHERMAL_STATUS_ERROR; + } + return static_cast<AThermalStatus>(status); +} + +/** + * Register the thermal status listener for thermal status change. + * + * @param manager The manager instance to use to register. + * acquired by {@link AThermal_acquireManager}. + * @param callback The callback function to be called when thermal status updated. + * @param data The data pointer to be passed when callback is called. + * + * @return 0 on success + * EINVAL if the listener and data pointer were previously added and not removed. + * EPERM if the required permission is not held. + * EPIPE if communication with the system service has failed. + */ +int AThermal_registerThermalStatusListener(AThermalManager *manager, + AThermal_StatusCallback callback, void *data) { + return manager->addListener(callback, data); +} + +/** + * Unregister the thermal status listener previously resgistered. + * + * @param manager The manager instance to use to unregister. + * acquired by {@link AThermal_acquireManager}. + * @param callback The callback function to be called when thermal status updated. + * @param data The data pointer to be passed when callback is called. + * + * @return 0 on success + * EINVAL if the listener and data pointer were not previously added. + * EPERM if the required permission is not held. + * EPIPE if communication with the system service has failed. + */ +int AThermal_unregisterThermalStatusListener(AThermalManager *manager, + AThermal_StatusCallback callback, void *data) { + return manager->removeListener(callback, data); +} diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp index 942eafdbc48d..15b473c2a6ab 100644 --- a/native/graphics/jni/Android.bp +++ b/native/graphics/jni/Android.bp @@ -24,12 +24,23 @@ cc_library_shared { // our source files // - srcs: ["bitmap.cpp"], + srcs: [ + "aassetstreamadaptor.cpp", + "bitmap.cpp", + "imagedecoder.cpp", + ], shared_libs: [ + "libandroid", "libandroid_runtime", + "libhwui", + "liblog", ], + header_libs: [ "libhwui_internal_headers" ], + + static_libs: ["libarect"], + arch: { arm: { // TODO: This is to work around b/24465209. Remove after root cause is fixed diff --git a/native/graphics/jni/aassetstreamadaptor.cpp b/native/graphics/jni/aassetstreamadaptor.cpp new file mode 100644 index 000000000000..016008bfce00 --- /dev/null +++ b/native/graphics/jni/aassetstreamadaptor.cpp @@ -0,0 +1,117 @@ +/* + * 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. + */ + +#include "aassetstreamadaptor.h" + +#include <log/log.h> + +AAssetStreamAdaptor::AAssetStreamAdaptor(AAsset* asset) + : mAAsset(asset) +{ +} + +bool AAssetStreamAdaptor::rewind() { + off64_t pos = AAsset_seek64(mAAsset, 0, SEEK_SET); + if (pos == (off64_t)-1) { + ALOGE("rewind failed!"); + return false; + } + return true; +} + +size_t AAssetStreamAdaptor::getLength() const { + return AAsset_getLength64(mAAsset); +} + +bool AAssetStreamAdaptor::isAtEnd() const { + return AAsset_getRemainingLength64(mAAsset) == 0; +} + +SkStreamRewindable* AAssetStreamAdaptor::onDuplicate() const { + // Cannot sensibly create a duplicate, since each AAssetStreamAdaptor + // would be modifying the same AAsset. + //return new AAssetStreamAdaptor(mAAsset); + return nullptr; +} + +bool AAssetStreamAdaptor::hasPosition() const { + return AAsset_seek64(mAAsset, 0, SEEK_CUR) != -1; +} + +size_t AAssetStreamAdaptor::getPosition() const { + const off64_t offset = AAsset_seek64(mAAsset, 0, SEEK_CUR); + if (offset == -1) { + ALOGE("getPosition failed!"); + return 0; + } + + return offset; +} + +bool AAssetStreamAdaptor::seek(size_t position) { + if (AAsset_seek64(mAAsset, position, SEEK_SET) == -1) { + ALOGE("seek failed!"); + return false; + } + + return true; +} + +bool AAssetStreamAdaptor::move(long offset) { + if (AAsset_seek64(mAAsset, offset, SEEK_CUR) == -1) { + ALOGE("move failed!"); + return false; + } + + return true; +} + +size_t AAssetStreamAdaptor::read(void* buffer, size_t size) { + ssize_t amount; + + if (!buffer) { + if (!size) { + return 0; + } + + // asset->seek returns new total offset + // we want to return amount that was skipped + const off64_t oldOffset = AAsset_seek64(mAAsset, 0, SEEK_CUR); + if (oldOffset == -1) { + ALOGE("seek(oldOffset) failed!"); + return 0; + } + + const off64_t newOffset = AAsset_seek64(mAAsset, size, SEEK_CUR); + if (-1 == newOffset) { + ALOGE("seek(%zu) failed!", size); + return 0; + } + amount = newOffset - oldOffset; + } else { + amount = AAsset_read(mAAsset, buffer, size); + } + + if (amount < 0) { + amount = 0; + } + return amount; +} + +const void* AAssetStreamAdaptor::getMemoryBase() { + return AAsset_getBuffer(mAAsset); +} + diff --git a/native/graphics/jni/aassetstreamadaptor.h b/native/graphics/jni/aassetstreamadaptor.h new file mode 100644 index 000000000000..c1fddb01344d --- /dev/null +++ b/native/graphics/jni/aassetstreamadaptor.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#pragma once + +#include "SkStream.h" + +#include <android/asset_manager.h> + +// Like AssetStreamAdaptor, but operates on AAsset, a public NDK API. +class AAssetStreamAdaptor : public SkStreamRewindable { +public: + /** + * Create an SkStream that reads from an AAsset. + * + * The AAsset must remain open for the lifetime of the Adaptor. The Adaptor + * does *not* close the AAsset. + */ + explicit AAssetStreamAdaptor(AAsset*); + + bool rewind() override; + size_t read(void* buffer, size_t size) override; + bool hasLength() const override { return true; } + size_t getLength() const override; + bool hasPosition() const override; + size_t getPosition() const override; + bool seek(size_t position) override; + bool move(long offset) override; + bool isAtEnd() const override; + const void* getMemoryBase() override; +protected: + SkStreamRewindable* onDuplicate() const override; + +private: + AAsset* mAAsset; +}; + diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp index ff14832a2f0f..b8eb543ff835 100644 --- a/native/graphics/jni/bitmap.cpp +++ b/native/graphics/jni/bitmap.cpp @@ -15,7 +15,9 @@ */ #include <android/bitmap.h> -#include <android/graphics/Bitmap.h> +#include <android/data_space.h> +#include <android/graphics/bitmap.h> +#include <android/data_space.h> int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, AndroidBitmapInfo* info) { @@ -24,21 +26,38 @@ int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, } if (info) { - android::bitmap::imageInfo(env, jbitmap, info); + *info = ABitmap_getInfoFromJava(env, jbitmap); } return ANDROID_BITMAP_RESULT_SUCCESS; } +int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) { + if (NULL == env || NULL == jbitmap) { + return ADATASPACE_UNKNOWN; + } + + android::graphics::Bitmap bitmap(env, jbitmap); + if (!bitmap.isValid()) { + return ADATASPACE_UNKNOWN; + } + + return bitmap.getDataSpace(); +} + int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr) { if (NULL == env || NULL == jbitmap) { return ANDROID_BITMAP_RESULT_BAD_PARAMETER; } - void* addr = android::bitmap::lockPixels(env, jbitmap); + android::graphics::Bitmap bitmap(env, jbitmap); + void* addr = bitmap.isValid() ? bitmap.getPixels() : nullptr; + if (!addr) { return ANDROID_BITMAP_RESULT_JNI_EXCEPTION; } + ABitmap_acquireRef(bitmap.get()); + if (addrPtr) { *addrPtr = addr; } @@ -50,9 +69,45 @@ int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap) { return ANDROID_BITMAP_RESULT_BAD_PARAMETER; } - bool unlocked = android::bitmap::unlockPixels(env, jbitmap); - if (!unlocked) { + android::graphics::Bitmap bitmap(env, jbitmap); + + if (!bitmap.isValid()) { return ANDROID_BITMAP_RESULT_JNI_EXCEPTION; } + + bitmap.notifyPixelsChanged(); + ABitmap_releaseRef(bitmap.get()); return ANDROID_BITMAP_RESULT_SUCCESS; } + +int AndroidBitmap_getHardwareBuffer(JNIEnv* env, jobject jbitmap, AHardwareBuffer** outBuffer) { + if (NULL == env || NULL == jbitmap || NULL == outBuffer) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + android::graphics::Bitmap bitmap(env, jbitmap); + + if (!bitmap.isValid()) { + return ANDROID_BITMAP_RESULT_JNI_EXCEPTION; + } + + *outBuffer = bitmap.getHardwareBuffer(); + return *outBuffer == NULL ? ANDROID_BITMAP_RESULT_BAD_PARAMETER : ANDROID_BITMAP_RESULT_SUCCESS; +} + +int AndroidBitmap_compress(const AndroidBitmapInfo* info, + int32_t dataSpace, + const void* pixels, + int32_t format, int32_t quality, + void* userContext, + AndroidBitmap_CompressWriteFunc fn) { + if (NULL == info || NULL == pixels || NULL == fn) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + if (quality < 0 || quality > 100) { + return ANDROID_BITMAP_RESULT_BAD_PARAMETER; + } + + return ABitmap_compress(info, (ADataSpace) dataSpace, pixels, + (AndroidBitmapCompressFormat) format, quality, userContext, fn); +} diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp new file mode 100644 index 000000000000..56f390698eb2 --- /dev/null +++ b/native/graphics/jni/imagedecoder.cpp @@ -0,0 +1,348 @@ +/* + * 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. + */ + +#include "aassetstreamadaptor.h" + +#include <android/asset_manager.h> +#include <android/bitmap.h> +#include <android/data_space.h> +#include <android/imagedecoder.h> +#include <MimeType.h> +#include <android/rect.h> +#include <hwui/ImageDecoder.h> +#include <log/log.h> +#include <SkAndroidCodec.h> +#include <utils/Color.h> + +#include <fcntl.h> +#include <limits> +#include <optional> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +using namespace android; + +int ResultToErrorCode(SkCodec::Result result) { + switch (result) { + case SkCodec::kIncompleteInput: + return ANDROID_IMAGE_DECODER_INCOMPLETE; + case SkCodec::kErrorInInput: + return ANDROID_IMAGE_DECODER_ERROR; + case SkCodec::kInvalidInput: + return ANDROID_IMAGE_DECODER_INVALID_INPUT; + case SkCodec::kCouldNotRewind: + return ANDROID_IMAGE_DECODER_SEEK_ERROR; + case SkCodec::kUnimplemented: + return ANDROID_IMAGE_DECODER_UNSUPPORTED_FORMAT; + case SkCodec::kInvalidConversion: + return ANDROID_IMAGE_DECODER_INVALID_CONVERSION; + case SkCodec::kInvalidParameters: + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + case SkCodec::kSuccess: + return ANDROID_IMAGE_DECODER_SUCCESS; + case SkCodec::kInvalidScale: + return ANDROID_IMAGE_DECODER_INVALID_SCALE; + case SkCodec::kInternalError: + return ANDROID_IMAGE_DECODER_INTERNAL_ERROR; + } +} + +static int createFromStream(std::unique_ptr<SkStreamRewindable> stream, AImageDecoder** outDecoder) { + SkCodec::Result result; + auto codec = SkCodec::MakeFromStream(std::move(stream), &result, nullptr, + SkCodec::SelectionPolicy::kPreferAnimation); + auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec), + SkAndroidCodec::ExifOrientationBehavior::kRespect); + if (!androidCodec) { + return ResultToErrorCode(result); + } + + // AImageDecoderHeaderInfo_getWidth/Height return an int32_t. Ensure that + // the conversion is safe. + const auto& info = androidCodec->getInfo(); + if (info.width() > std::numeric_limits<int32_t>::max() + || info.height() > std::numeric_limits<int32_t>::max()) { + return ANDROID_IMAGE_DECODER_INVALID_INPUT; + } + + *outDecoder = reinterpret_cast<AImageDecoder*>(new ImageDecoder(std::move(androidCodec))); + return ANDROID_IMAGE_DECODER_SUCCESS; +} + +int AImageDecoder_createFromAAsset(AAsset* asset, AImageDecoder** outDecoder) { + if (!asset || !outDecoder) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + *outDecoder = nullptr; + + auto stream = std::make_unique<AAssetStreamAdaptor>(asset); + return createFromStream(std::move(stream), outDecoder); +} + +static bool isSeekable(int descriptor) { + return ::lseek64(descriptor, 0, SEEK_CUR) != -1; +} + +int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) { + if (fd <= 0 || !outDecoder) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + struct stat fdStat; + if (fstat(fd, &fdStat) == -1) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + if (!isSeekable(fd)) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + // SkFILEStream will close its descriptor. Duplicate it so the client will + // still be responsible for closing the original. + int dupDescriptor = fcntl(fd, F_DUPFD_CLOEXEC, 0); + FILE* file = fdopen(dupDescriptor, "r"); + if (!file) { + close(dupDescriptor); + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + auto stream = std::unique_ptr<SkStreamRewindable>(new SkFILEStream(file)); + return createFromStream(std::move(stream), outDecoder); +} + +int AImageDecoder_createFromBuffer(const void* buffer, size_t length, + AImageDecoder** outDecoder) { + if (!buffer || !length || !outDecoder) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + *outDecoder = nullptr; + + // The client is expected to keep the buffer alive as long as the + // AImageDecoder, so we do not need to copy the buffer. + auto stream = std::unique_ptr<SkStreamRewindable>( + new SkMemoryStream(buffer, length, false /* copyData */)); + return createFromStream(std::move(stream), outDecoder); +} + +static ImageDecoder* toDecoder(AImageDecoder* d) { + return reinterpret_cast<ImageDecoder*>(d); +} + +static const ImageDecoder* toDecoder(const AImageDecoder* d) { + return reinterpret_cast<const ImageDecoder*>(d); +} + +// Note: This differs from the version in android_bitmap.cpp in that this +// version returns kGray_8_SkColorType for ANDROID_BITMAP_FORMAT_A_8. SkCodec +// allows decoding single channel images to gray, which Android then treats +// as A_8/ALPHA_8. +static SkColorType getColorType(AndroidBitmapFormat format) { + switch (format) { + case ANDROID_BITMAP_FORMAT_RGBA_8888: + return kN32_SkColorType; + case ANDROID_BITMAP_FORMAT_RGB_565: + return kRGB_565_SkColorType; + case ANDROID_BITMAP_FORMAT_RGBA_4444: + return kARGB_4444_SkColorType; + case ANDROID_BITMAP_FORMAT_A_8: + return kGray_8_SkColorType; + case ANDROID_BITMAP_FORMAT_RGBA_F16: + return kRGBA_F16_SkColorType; + default: + return kUnknown_SkColorType; + } +} + +int AImageDecoder_setAndroidBitmapFormat(AImageDecoder* decoder, int32_t format) { + if (!decoder || format < ANDROID_BITMAP_FORMAT_NONE + || format > ANDROID_BITMAP_FORMAT_RGBA_F16) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + return toDecoder(decoder)->setOutColorType(getColorType((AndroidBitmapFormat) format)) + ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION; +} + +int AImageDecoder_setDataSpace(AImageDecoder* decoder, int32_t dataspace) { + sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace((android_dataspace)dataspace); + // 0 is ADATASPACE_UNKNOWN. We need an explicit request for an ADataSpace. + if (!decoder || !dataspace || !cs) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + ImageDecoder* imageDecoder = toDecoder(decoder); + imageDecoder->setOutColorSpace(std::move(cs)); + return ANDROID_IMAGE_DECODER_SUCCESS; +} + +const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo(const AImageDecoder* decoder) { + return reinterpret_cast<const AImageDecoderHeaderInfo*>(decoder); +} + +static const ImageDecoder* toDecoder(const AImageDecoderHeaderInfo* info) { + return reinterpret_cast<const ImageDecoder*>(info); +} + +int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo* info) { + if (!info) { + return 0; + } + return toDecoder(info)->mCodec->getInfo().width(); +} + +int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo* info) { + if (!info) { + return 0; + } + return toDecoder(info)->mCodec->getInfo().height(); +} + +const char* AImageDecoderHeaderInfo_getMimeType(const AImageDecoderHeaderInfo* info) { + if (!info) { + return nullptr; + } + return getMimeType(toDecoder(info)->mCodec->getEncodedFormat()); +} + +int32_t AImageDecoderHeaderInfo_getDataSpace(const AImageDecoderHeaderInfo* info) { + if (!info) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + // Note: This recomputes the color type because it's possible the client has + // changed the output color type, so we cannot rely on it. Alternatively, + // we could store the ADataSpace in the ImageDecoder. + const ImageDecoder* imageDecoder = toDecoder(info); + SkColorType colorType = imageDecoder->mCodec->computeOutputColorType(kN32_SkColorType); + sk_sp<SkColorSpace> colorSpace = imageDecoder->getDefaultColorSpace(); + return uirenderer::ColorSpaceToADataSpace(colorSpace.get(), colorType); +} + +// FIXME: Share with getFormat in android_bitmap.cpp? +static AndroidBitmapFormat getFormat(SkColorType colorType) { + switch (colorType) { + case kN32_SkColorType: + return ANDROID_BITMAP_FORMAT_RGBA_8888; + case kRGB_565_SkColorType: + return ANDROID_BITMAP_FORMAT_RGB_565; + case kARGB_4444_SkColorType: + return ANDROID_BITMAP_FORMAT_RGBA_4444; + case kAlpha_8_SkColorType: + return ANDROID_BITMAP_FORMAT_A_8; + case kRGBA_F16_SkColorType: + return ANDROID_BITMAP_FORMAT_RGBA_F16; + default: + return ANDROID_BITMAP_FORMAT_NONE; + } +} + +int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat(const AImageDecoderHeaderInfo* info) { + if (!info) { + return ANDROID_BITMAP_FORMAT_NONE; + } + return getFormat(toDecoder(info)->mCodec->computeOutputColorType(kN32_SkColorType)); +} + +int AImageDecoderHeaderInfo_getAlphaFlags(const AImageDecoderHeaderInfo* info) { + if (!info) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + switch (toDecoder(info)->mCodec->getInfo().alphaType()) { + case kUnknown_SkAlphaType: + LOG_ALWAYS_FATAL("Invalid alpha type"); + return ANDROID_IMAGE_DECODER_INTERNAL_ERROR; + case kUnpremul_SkAlphaType: + // fall through. premul is the default. + case kPremul_SkAlphaType: + return ANDROID_BITMAP_FLAGS_ALPHA_PREMUL; + case kOpaque_SkAlphaType: + return ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE; + } +} + +int AImageDecoder_setUnpremultipliedRequired(AImageDecoder* decoder, bool required) { + if (!decoder) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + return toDecoder(decoder)->setUnpremultipliedRequired(required) + ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_CONVERSION; +} + +int AImageDecoder_setTargetSize(AImageDecoder* decoder, int32_t width, int32_t height) { + if (!decoder) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + return toDecoder(decoder)->setTargetSize(width, height) + ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_INVALID_SCALE; +} + +int AImageDecoder_computeSampledSize(const AImageDecoder* decoder, int sampleSize, + int32_t* width, int32_t* height) { + if (!decoder || !width || !height || sampleSize < 1) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + SkISize size = toDecoder(decoder)->mCodec->getSampledDimensions(sampleSize); + *width = size.width(); + *height = size.height(); + return ANDROID_IMAGE_DECODER_SUCCESS; +} + +int AImageDecoder_setCrop(AImageDecoder* decoder, ARect crop) { + if (!decoder) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + SkIRect cropIRect; + cropIRect.setLTRB(crop.left, crop.top, crop.right, crop.bottom); + SkIRect* cropPtr = cropIRect == SkIRect::MakeEmpty() ? nullptr : &cropIRect; + return toDecoder(decoder)->setCropRect(cropPtr) + ? ANDROID_IMAGE_DECODER_SUCCESS : ANDROID_IMAGE_DECODER_BAD_PARAMETER; +} + + +size_t AImageDecoder_getMinimumStride(AImageDecoder* decoder) { + if (!decoder) { + return 0; + } + + SkImageInfo info = toDecoder(decoder)->getOutputInfo(); + return info.minRowBytes(); +} + +int AImageDecoder_decodeImage(AImageDecoder* decoder, + void* pixels, size_t stride, + size_t size) { + if (!decoder || !pixels || !stride) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + ImageDecoder* imageDecoder = toDecoder(decoder); + + SkImageInfo info = imageDecoder->getOutputInfo(); + size_t minSize = info.computeByteSize(stride); + if (SkImageInfo::ByteSizeOverflowed(minSize) || size < minSize || !info.validRowBytes(stride)) { + return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + } + + return ResultToErrorCode(imageDecoder->decode(pixels, stride)); +} + +void AImageDecoder_delete(AImageDecoder* decoder) { + delete toDecoder(decoder); +} diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt index a601d8af2830..01c14770bebd 100644 --- a/native/graphics/jni/libjnigraphics.map.txt +++ b/native/graphics/jni/libjnigraphics.map.txt @@ -1,8 +1,30 @@ LIBJNIGRAPHICS { global: + AImageDecoder_createFromAAsset; # introduced=30 + AImageDecoder_createFromFd; # introduced=30 + AImageDecoder_createFromBuffer; # introduced=30 + AImageDecoder_delete; # introduced=30 + AImageDecoder_setAndroidBitmapFormat; # introduced=30 + AImageDecoder_setUnpremultipliedRequired; # introduced=30 + AImageDecoder_setDataSpace; # introduced=30 + AImageDecoder_getHeaderInfo; # introduced=30 + AImageDecoder_getMinimumStride; # introduced=30 + AImageDecoder_decodeImage; # introduced=30 + AImageDecoder_setTargetSize; # introduced=30 + AImageDecoder_computeSampledSize; # introduced=30 + AImageDecoder_setCrop; # introduced=30 + AImageDecoderHeaderInfo_getWidth; # introduced=30 + AImageDecoderHeaderInfo_getHeight; # introduced=30 + AImageDecoderHeaderInfo_getMimeType; # introduced=30 + AImageDecoderHeaderInfo_getAlphaFlags; # introduced=30 + AImageDecoderHeaderInfo_getAndroidBitmapFormat; # introduced=30 + AImageDecoderHeaderInfo_getDataSpace; # introduced=30 AndroidBitmap_getInfo; + AndroidBitmap_getDataSpace; AndroidBitmap_lockPixels; AndroidBitmap_unlockPixels; + AndroidBitmap_compress; # introduced=30 + AndroidBitmap_getHardwareBuffer; #introduced=30 local: *; }; diff --git a/native/webview/plat_support/Android.bp b/native/webview/plat_support/Android.bp index 88decc86c387..1a3b36d046e1 100644 --- a/native/webview/plat_support/Android.bp +++ b/native/webview/plat_support/Android.bp @@ -30,12 +30,14 @@ cc_library_shared { "graphic_buffer_impl.cpp", ], + header_libs: [ "libhwui_internal_headers" ], + shared_libs: [ "libandroidfw", - "libandroid_runtime", "libcutils", "libhwui", "liblog", + "libnativehelper", "libui", "libutils", "libvulkan", diff --git a/native/webview/plat_support/graphics_utils.cpp b/native/webview/plat_support/graphics_utils.cpp index 56825cee4520..8d7a59e46e72 100644 --- a/native/webview/plat_support/graphics_utils.cpp +++ b/native/webview/plat_support/graphics_utils.cpp @@ -25,11 +25,9 @@ #include <cstdlib> #include <jni.h> #include <utils/Log.h> -#include "android/graphics/GraphicsJNI.h" +#include "GraphicsJNI.h" #include "graphic_buffer_impl.h" #include "SkCanvasStateUtils.h" -#include "SkGraphics.h" -#include "SkPicture.h" #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) |