Flutter Engine
The Flutter Engine
surface_glue_android.cpp
Go to the documentation of this file.
1/*
2* Copyright 2016 Google Inc.
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
9
10#include <jni.h>
11#include <pthread.h>
12#include <stdio.h>
13#include <unistd.h>
14#include <unordered_map>
15
16#include <android/asset_manager.h>
17#include <android/asset_manager_jni.h>
18#include <android/input.h>
19#include <android/keycodes.h>
20#include <android/looper.h>
21#include <android/native_window_jni.h>
22
25#include "src/base/SkUTF.h"
29
30
31namespace sk_app {
32
33static void config_resource_mgr(JNIEnv* env, jobject assetManager) {
34 static AAssetManager* gAAssetManager = nullptr;
35 SkASSERT(assetManager);
36 gAAssetManager = AAssetManager_fromJava(env, assetManager);
37 SkASSERT(gAAssetManager);
38 gResourceFactory = [](const char* resource) -> sk_sp<SkData> {
39 if (!gAAssetManager) {
40 return nullptr;
41 }
42 SkString path = SkStringPrintf("resources/%s", resource);
43 AAsset* asset = AAssetManager_open(gAAssetManager, path.c_str(), AASSET_MODE_STREAMING);
44 if (!asset) {
45 return nullptr;
46 }
47 size_t size = SkToSizeT(AAsset_getLength(asset));
49 (void)AAsset_read(asset, data->writable_data(), size);
50 AAsset_close(asset);
51 return data;
52 };
53}
54
55static const int LOOPER_ID_MESSAGEPIPE = 1;
56
57static const std::unordered_map<int, skui::Key> ANDROID_TO_WINDOW_KEYMAP({
58 {AKEYCODE_SOFT_LEFT, skui::Key::kLeft },
59 {AKEYCODE_SOFT_RIGHT, skui::Key::kRight}
60});
61
62static const std::unordered_map<int, skui::InputState> ANDROID_TO_WINDOW_STATEMAP({
63 {AMOTION_EVENT_ACTION_DOWN, skui::InputState::kDown },
64 {AMOTION_EVENT_ACTION_POINTER_DOWN, skui::InputState::kDown },
65 {AMOTION_EVENT_ACTION_UP, skui::InputState::kUp },
66 {AMOTION_EVENT_ACTION_POINTER_UP, skui::InputState::kUp },
67 {AMOTION_EVENT_ACTION_MOVE, skui::InputState::kMove },
68 {AMOTION_EVENT_ACTION_CANCEL, skui::InputState::kUp },
69});
70
71SkiaAndroidApp::SkiaAndroidApp(JNIEnv* env, jobject androidApp) {
72 env->GetJavaVM(&fJavaVM);
73 fAndroidApp = env->NewGlobalRef(androidApp);
74 jclass cls = env->GetObjectClass(fAndroidApp);
75 fSetTitleMethodID = env->GetMethodID(cls, "setTitle", "(Ljava/lang/String;)V");
76 fSetStateMethodID = env->GetMethodID(cls, "setState", "(Ljava/lang/String;)V");
77 fNativeWindow = nullptr;
78 pthread_create(&fThread, nullptr, pthread_main, this);
79}
80
81SkiaAndroidApp::~SkiaAndroidApp() {
82 fPThreadEnv->DeleteGlobalRef(fAndroidApp);
83 if (fWindow) {
84 fWindow->detach();
85 }
86 if (fNativeWindow) {
87 ANativeWindow_release(fNativeWindow);
88 fNativeWindow = nullptr;
89 }
90 if (fApp) {
91 delete fApp;
92 }
93}
94
95void SkiaAndroidApp::setTitle(const char* title) const {
96 jstring titleString = fPThreadEnv->NewStringUTF(title);
97 fPThreadEnv->CallVoidMethod(fAndroidApp, fSetTitleMethodID, titleString);
98 fPThreadEnv->DeleteLocalRef(titleString);
99}
100
101void SkiaAndroidApp::setUIState(const char* state) const {
102 jstring jstr = fPThreadEnv->NewStringUTF(state);
103 fPThreadEnv->CallVoidMethod(fAndroidApp, fSetStateMethodID, jstr);
104 fPThreadEnv->DeleteLocalRef(jstr);
105}
106
108 SkDEBUGCODE(auto writeSize =) write(fPipes[1], &message, sizeof(message));
109 SkASSERT(writeSize == sizeof(message));
110}
111
113 SkDEBUGCODE(auto readSize =) read(fPipes[0], message, sizeof(Message));
114 SkASSERT(readSize == sizeof(Message));
115}
116
117int SkiaAndroidApp::message_callback(int fd, int events, void* data) {
118 auto skiaAndroidApp = (SkiaAndroidApp*)data;
120 skiaAndroidApp->readMessage(&message);
121 SkASSERT(message.fType != kUndefined);
122
123 switch (message.fType) {
124 case kDestroyApp: {
125 delete skiaAndroidApp;
126 pthread_exit(nullptr);
127 }
128 case kContentInvalidated: {
129 ((Window_android*)skiaAndroidApp->fWindow)->paintIfNeeded();
130 break;
131 }
132 case kSurfaceCreated: {
133 SkASSERT(!skiaAndroidApp->fNativeWindow && message.fNativeWindow);
134 skiaAndroidApp->fNativeWindow = message.fNativeWindow;
135 auto window_android = (Window_android*)skiaAndroidApp->fWindow;
136 window_android->initDisplay(skiaAndroidApp->fNativeWindow);
137 ((Window_android*)skiaAndroidApp->fWindow)->paintIfNeeded();
138 break;
139 }
140 case kSurfaceChanged: {
141 SkASSERT(message.fNativeWindow);
142 int width = ANativeWindow_getWidth(skiaAndroidApp->fNativeWindow);
143 int height = ANativeWindow_getHeight(skiaAndroidApp->fNativeWindow);
144 auto window_android = (Window_android*)skiaAndroidApp->fWindow;
145 if (message.fNativeWindow != skiaAndroidApp->fNativeWindow) {
146 window_android->onDisplayDestroyed();
147 ANativeWindow_release(skiaAndroidApp->fNativeWindow);
148 skiaAndroidApp->fNativeWindow = message.fNativeWindow;
149 window_android->initDisplay(skiaAndroidApp->fNativeWindow);
150 }
151 window_android->onResize(width, height);
152 window_android->paintIfNeeded();
153 break;
154 }
155 case kSurfaceDestroyed: {
156 if (skiaAndroidApp->fNativeWindow) {
157 auto window_android = (Window_android*)skiaAndroidApp->fWindow;
158 window_android->onDisplayDestroyed();
159 ANativeWindow_release(skiaAndroidApp->fNativeWindow);
160 skiaAndroidApp->fNativeWindow = nullptr;
161 }
162 break;
163 }
164 case kKeyPressed: {
165 auto it = ANDROID_TO_WINDOW_KEYMAP.find(message.fKeycode);
167 // No modifier is supported so far
168 skiaAndroidApp->fWindow->onKey(it->second, skui::InputState::kDown, skui::ModifierKey::kNone);
169 skiaAndroidApp->fWindow->onKey(it->second, skui::InputState::kUp, skui::ModifierKey::kNone);
170 break;
171 }
172 case kTouched: {
173 auto it = ANDROID_TO_WINDOW_STATEMAP.find(message.fTouchState);
174 if (it != ANDROID_TO_WINDOW_STATEMAP.end()) {
175 skiaAndroidApp->fWindow->onTouch(message.fTouchOwner, it->second, message.fTouchX,
176 message.fTouchY);
177 } else {
178 SkDebugf("Unknown Touch State: %d\n", message.fTouchState);
179 }
180 break;
181 }
182 case kUIStateChanged: {
183 skiaAndroidApp->fWindow->onUIStateChanged(*message.stateName, *message.stateValue);
184 delete message.stateName;
185 delete message.stateValue;
186 break;
187 }
188 default: {
189 // do nothing
190 }
191 }
192
193 return 1; // continue receiving callbacks
194}
195
196void* SkiaAndroidApp::pthread_main(void* arg) {
197 SkDebugf("pthread_main begins");
198
199 auto skiaAndroidApp = (SkiaAndroidApp*)arg;
200
201 // Because JNIEnv is thread sensitive, we need AttachCurrentThread to set our fPThreadEnv
202 skiaAndroidApp->fJavaVM->AttachCurrentThread(&(skiaAndroidApp->fPThreadEnv), nullptr);
203
204 ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
205 pipe(skiaAndroidApp->fPipes);
206 ALooper_addFd(looper, skiaAndroidApp->fPipes[0], LOOPER_ID_MESSAGEPIPE, ALOOPER_EVENT_INPUT,
207 message_callback, skiaAndroidApp);
208
209 static const char* gCmdLine[] = {
210 "viewer",
211 // TODO: figure out how to use am start with extra params to pass in additional arguments at
212 // runtime. Or better yet make an in app switch to enable
213 // "--atrace",
214 };
215
216 skiaAndroidApp->fApp = Application::Create(std::size(gCmdLine),
217 const_cast<char**>(gCmdLine),
218 skiaAndroidApp);
219
220 while (true) {
221 int ident = 0;
222 while (ident == ALOOPER_POLL_CALLBACK) {
223 ident = ALooper_pollOnce(0, nullptr, nullptr, nullptr);
224 }
225
226 if (ident >= 0) {
227 SkDebugf("Unhandled ALooper_pollOnce ident=%d !", ident);
228 } else {
229 skiaAndroidApp->fApp->onIdle();
230 }
231 }
232}
233
234extern "C" // extern "C" is needed for JNI (although the method itself is in C++)
235 JNIEXPORT jlong JNICALL
237 jobject application,
238 jobject assetManager) {
239 config_resource_mgr(env, assetManager);
240 SkiaAndroidApp* skiaAndroidApp = new SkiaAndroidApp(env, application);
241 return (jlong)((size_t)skiaAndroidApp);
242}
243
245 JNIEnv* env, jobject application, jlong handle) {
246 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
247 skiaAndroidApp->postMessage(Message(kDestroyApp));
248}
249
251 JNIEnv* env, jobject activity, jlong handle, jobject surface) {
252 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
254 message.fNativeWindow = ANativeWindow_fromSurface(env, surface);
255 skiaAndroidApp->postMessage(message);
256}
257
259 JNIEnv* env, jobject activity, jlong handle, jobject surface) {
260 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
262 message.fNativeWindow = ANativeWindow_fromSurface(env, surface);
263 skiaAndroidApp->postMessage(message);
264}
265
267 JNIEnv* env, jobject activity, jlong handle) {
268 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
269 skiaAndroidApp->postMessage(Message(kSurfaceDestroyed));
270}
271
272extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onKeyPressed(JNIEnv* env,
273 jobject activity,
274 jlong handle,
275 jint keycode) {
276 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
278 message.fKeycode = keycode;
279 skiaAndroidApp->postMessage(message);
280}
281
282extern "C" JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onTouched(
283 JNIEnv* env, jobject activity, jlong handle, jint owner, jint state, jfloat x, jfloat y) {
284 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
286 message.fTouchOwner = owner;
287 message.fTouchState = state;
288 message.fTouchX = x;
289 message.fTouchY = y;
290 skiaAndroidApp->postMessage(message);
291}
292
294 JNIEnv* env, jobject activity, jlong handle, jstring stateName, jstring stateValue) {
295 auto skiaAndroidApp = (SkiaAndroidApp*)handle;
297 const char* nameChars = env->GetStringUTFChars(stateName, nullptr);
298 const char* valueChars = env->GetStringUTFChars(stateValue, nullptr);
299 message.stateName = new SkString(nameChars);
300 message.stateValue = new SkString(valueChars);
301 skiaAndroidApp->postMessage(message);
302 env->ReleaseStringUTFChars(stateName, nameChars);
303 env->ReleaseStringUTFChars(stateValue, valueChars);
304}
305
306} // namespace sk_app
sk_sp< SkData >(* gResourceFactory)(const char *)
Definition: Resources.cpp:21
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static bool read(SkStream *stream, void *buffer, size_t amount)
static SkString resource(SkPDFResourceType type, int index)
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
constexpr size_t SkToSizeT(S x)
Definition: SkTo.h:31
static sk_sp< SkData > MakeUninitialized(size_t length)
Definition: SkData.cpp:116
static Application * Create(int argc, char **argv, void *platformData)
void detach()
Definition: Window.cpp:25
VkSurfaceKHR surface
Definition: main.cc:49
AtkStateType state
if(end==-1)
Win32Message message
double y
double x
Definition: __init__.py:1
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceChanged(JNIEnv *env, jobject activity, jlong handle, jobject surface)
static const int LOOPER_ID_MESSAGEPIPE
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onKeyPressed(JNIEnv *env, jobject activity, jlong handle, jint keycode)
static void config_resource_mgr(JNIEnv *env, jobject assetManager)
static const std::unordered_map< int, skui::InputState > ANDROID_TO_WINDOW_STATEMAP({ {AMOTION_EVENT_ACTION_DOWN, skui::InputState::kDown }, {AMOTION_EVENT_ACTION_POINTER_DOWN, skui::InputState::kDown }, {AMOTION_EVENT_ACTION_UP, skui::InputState::kUp }, {AMOTION_EVENT_ACTION_POINTER_UP, skui::InputState::kUp }, {AMOTION_EVENT_ACTION_MOVE, skui::InputState::kMove }, {AMOTION_EVENT_ACTION_CANCEL, skui::InputState::kUp }, })
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerApplication_destroyNativeApp(JNIEnv *env, jobject application, jlong handle)
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onUIStateChanged(JNIEnv *env, jobject activity, jlong handle, jstring stateName, jstring stateValue)
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceCreated(JNIEnv *env, jobject activity, jlong handle, jobject surface)
JNIEXPORT jlong JNICALL Java_org_skia_viewer_ViewerApplication_createNativeApp(JNIEnv *env, jobject application, jobject assetManager)
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onTouched(JNIEnv *env, jobject activity, jlong handle, jint owner, jint state, jfloat x, jfloat y)
JNIEXPORT void JNICALL Java_org_skia_viewer_ViewerActivity_onSurfaceDestroyed(JNIEnv *env, jobject activity, jlong handle)
static const std::unordered_map< int, skui::Key > ANDROID_TO_WINDOW_KEYMAP({ {AKEYCODE_SOFT_LEFT, skui::Key::kLeft }, {AKEYCODE_SOFT_RIGHT, skui::Key::kRight} })
int32_t height
int32_t width
void write(SkWStream *wStream, const T &text)
Definition: skqp.cpp:188
void setUIState(const char *state) const
SkiaAndroidApp(JNIEnv *env, jobject androidApp)
void setTitle(const char *title) const
void readMessage(Message *message) const
void postMessage(const Message &message) const
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63