mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 12:37:44 +00:00
Ladybird/Android: Move common service functionality to a base class
Create LadybirdServiceBase to hold the standard "set resource dir" and "init ipc sockets" service functionality that will be common between the WebContent, RequestServer, and WebSocket services. Refactor the handler class slightly to avoid the HandlerLeak lint by making the class a static class inside the companion object and use a WeakReference to the service instead of a strong one.
This commit is contained in:
parent
315ad2d391
commit
da8f450335
8 changed files with 117 additions and 91 deletions
|
@ -8,4 +8,4 @@
|
||||||
|
|
||||||
#include <AK/Error.h>
|
#include <AK/Error.h>
|
||||||
|
|
||||||
ErrorOr<int> web_content_main(int ipc_socket, int fd_passing_socket);
|
ErrorOr<int> service_main(int ipc_socket, int fd_passing_socket);
|
|
@ -4,17 +4,17 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "WebContentService.h"
|
#include "LadybirdServiceBase.h"
|
||||||
#include <AK/Atomic.h>
|
#include <AK/Atomic.h>
|
||||||
#include <AK/Format.h>
|
#include <AK/Format.h>
|
||||||
#include <Ladybird/Utilities.h>
|
#include <Ladybird/Utilities.h>
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
extern "C" JNIEXPORT void JNICALL
|
||||||
Java_org_serenityos_ladybird_WebContentService_nativeThreadLoop(JNIEnv*, jobject /* thiz */, jint ipc_socket, jint fd_passing_socket)
|
Java_org_serenityos_ladybird_LadybirdServiceBase_nativeThreadLoop(JNIEnv*, jobject /* thiz */, jint ipc_socket, jint fd_passing_socket)
|
||||||
{
|
{
|
||||||
dbgln("New binding received, sockets {} and {}", ipc_socket, fd_passing_socket);
|
dbgln("New binding received, sockets {} and {}", ipc_socket, fd_passing_socket);
|
||||||
auto ret = web_content_main(ipc_socket, fd_passing_socket);
|
auto ret = service_main(ipc_socket, fd_passing_socket);
|
||||||
if (ret.is_error()) {
|
if (ret.is_error()) {
|
||||||
warnln("Runtime Error: {}", ret.release_error());
|
warnln("Runtime Error: {}", ret.release_error());
|
||||||
} else {
|
} else {
|
||||||
|
@ -23,7 +23,7 @@ Java_org_serenityos_ladybird_WebContentService_nativeThreadLoop(JNIEnv*, jobject
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL
|
extern "C" JNIEXPORT void JNICALL
|
||||||
Java_org_serenityos_ladybird_WebContentService_initNativeCode(JNIEnv* env, jobject /* thiz */, jstring resource_dir, jstring tag_name)
|
Java_org_serenityos_ladybird_LadybirdServiceBase_initNativeCode(JNIEnv* env, jobject /* thiz */, jstring resource_dir, jstring tag_name)
|
||||||
{
|
{
|
||||||
static Atomic<bool> s_initialized_flag { false };
|
static Atomic<bool> s_initialized_flag { false };
|
||||||
if (s_initialized_flag.exchange(true) == true) {
|
if (s_initialized_flag.exchange(true) == true) {
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "WebContentService.h"
|
#include "LadybirdServiceBase.h"
|
||||||
#include <AK/LexicalPath.h>
|
#include <AK/LexicalPath.h>
|
||||||
#include <Ladybird/FontPlugin.h>
|
#include <Ladybird/FontPlugin.h>
|
||||||
#include <Ladybird/HelperProcess.h>
|
#include <Ladybird/HelperProcess.h>
|
||||||
|
@ -38,7 +38,7 @@ class NullResourceConnector : public Web::ResourceLoaderConnector {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ErrorOr<int> web_content_main(int ipc_socket, int fd_passing_socket)
|
ErrorOr<int> service_main(int ipc_socket, int fd_passing_socket)
|
||||||
{
|
{
|
||||||
Core::EventLoop event_loop;
|
Core::EventLoop event_loop;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.serenityos.ladybird
|
||||||
|
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
import android.os.ParcelFileDescriptor
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.Message
|
||||||
|
import android.os.Messenger
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
const val MSG_SET_RESOURCE_ROOT = 1
|
||||||
|
const val MSG_TRANSFER_SOCKETS = 2
|
||||||
|
|
||||||
|
abstract class LadybirdServiceBase(protected val TAG: String) : Service() {
|
||||||
|
private val threadPool = Executors.newCachedThreadPool()
|
||||||
|
private lateinit var resourceDir: String
|
||||||
|
|
||||||
|
override fun onCreate() {
|
||||||
|
super.onCreate()
|
||||||
|
Log.i(TAG, "Creating Service")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
Log.i(TAG, "Destroying Service")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
Log.i(TAG, "Start command received")
|
||||||
|
return super.onStartCommand(intent, flags, startId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleTransferSockets(msg: Message) {
|
||||||
|
val bundle = msg.data
|
||||||
|
// FIXME: Handle garbage messages from wierd clients
|
||||||
|
val ipcSocket = bundle.getParcelable<ParcelFileDescriptor>("IPC_SOCKET")!!
|
||||||
|
val fdSocket = bundle.getParcelable<ParcelFileDescriptor>("FD_PASSING_SOCKET")!!
|
||||||
|
createThread(ipcSocket, fdSocket)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleSetResourceRoot(msg: Message) {
|
||||||
|
// FIXME: Handle this being already set, not being present, etc
|
||||||
|
resourceDir = msg.data.getString("PATH")!!
|
||||||
|
|
||||||
|
initNativeCode(resourceDir, TAG)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(p0: Intent?): IBinder? {
|
||||||
|
// FIXME: Check the intent to make sure it's legit
|
||||||
|
return Messenger(IncomingHandler(WeakReference(this))).binder
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun createThread(ipcSocket: ParcelFileDescriptor, fdSocket: ParcelFileDescriptor) {
|
||||||
|
threadPool.execute {
|
||||||
|
nativeThreadLoop(ipcSocket.detachFd(), fdSocket.detachFd())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private external fun nativeThreadLoop(ipcSocket: Int, fdPassingSocket: Int)
|
||||||
|
private external fun initNativeCode(resourceDir: String, tagName: String);
|
||||||
|
|
||||||
|
abstract fun handleServiceSpecificMessage(msg: Message): Boolean
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
class IncomingHandler(private val service: WeakReference<LadybirdServiceBase>) :
|
||||||
|
Handler(Looper.getMainLooper()) {
|
||||||
|
override fun handleMessage(msg: Message) {
|
||||||
|
when (msg.what) {
|
||||||
|
MSG_TRANSFER_SOCKETS -> service.get()?.handleTransferSockets(msg)
|
||||||
|
?: super.handleMessage(msg)
|
||||||
|
|
||||||
|
MSG_SET_RESOURCE_ROOT -> service.get()?.handleSetResourceRoot(msg)
|
||||||
|
?: super.handleMessage(msg)
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
val ret = service.get()?.handleServiceSpecificMessage(msg)
|
||||||
|
if (ret == null || !ret)
|
||||||
|
super.handleMessage(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,15 +13,15 @@ import android.os.Message
|
||||||
import android.os.Messenger
|
import android.os.Messenger
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
|
|
||||||
class WebContentServiceConnection(
|
class LadybirdServiceConnection(
|
||||||
private var ipcFd: Int,
|
private var ipcFd: Int,
|
||||||
private var fdPassingFd: Int,
|
private var fdPassingFd: Int,
|
||||||
private var resourceDir: String
|
private var resourceDir: String
|
||||||
) :
|
) :
|
||||||
ServiceConnection {
|
ServiceConnection {
|
||||||
var boundToWebContent: Boolean = false
|
var boundToService: Boolean = false
|
||||||
var onDisconnect: () -> Unit = {}
|
var onDisconnect: () -> Unit = {}
|
||||||
private var webContentService: Messenger? = null
|
private var service: Messenger? = null
|
||||||
|
|
||||||
override fun onServiceConnected(className: ComponentName, svc: IBinder) {
|
override fun onServiceConnected(className: ComponentName, svc: IBinder) {
|
||||||
// This is called when the connection with the service has been
|
// This is called when the connection with the service has been
|
||||||
|
@ -29,24 +29,24 @@ class WebContentServiceConnection(
|
||||||
// interact with the service. We are communicating with the
|
// interact with the service. We are communicating with the
|
||||||
// service using a Messenger, so here we get a client-side
|
// service using a Messenger, so here we get a client-side
|
||||||
// representation of that from the raw IBinder object.
|
// representation of that from the raw IBinder object.
|
||||||
webContentService = Messenger(svc)
|
service = Messenger(svc)
|
||||||
boundToWebContent = true
|
boundToService = true
|
||||||
|
|
||||||
val init = Message.obtain(null, MSG_SET_RESOURCE_ROOT)
|
val init = Message.obtain(null, MSG_SET_RESOURCE_ROOT)
|
||||||
init.data.putString("PATH", resourceDir)
|
init.data.putString("PATH", resourceDir)
|
||||||
webContentService!!.send(init)
|
service!!.send(init)
|
||||||
|
|
||||||
val msg = Message.obtain(null, MSG_TRANSFER_SOCKETS)
|
val msg = Message.obtain(null, MSG_TRANSFER_SOCKETS)
|
||||||
msg.data.putParcelable("IPC_SOCKET", ParcelFileDescriptor.adoptFd(ipcFd))
|
msg.data.putParcelable("IPC_SOCKET", ParcelFileDescriptor.adoptFd(ipcFd))
|
||||||
msg.data.putParcelable("FD_PASSING_SOCKET", ParcelFileDescriptor.adoptFd(fdPassingFd))
|
msg.data.putParcelable("FD_PASSING_SOCKET", ParcelFileDescriptor.adoptFd(fdPassingFd))
|
||||||
webContentService!!.send(msg)
|
service!!.send(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceDisconnected(className: ComponentName) {
|
override fun onServiceDisconnected(className: ComponentName) {
|
||||||
// This is called when the connection with the service has been
|
// This is called when the connection with the service has been
|
||||||
// unexpectedly disconnected; that is, its process crashed.
|
// unexpectedly disconnected; that is, its process crashed.
|
||||||
webContentService = null
|
service = null
|
||||||
boundToWebContent = false
|
boundToService = false
|
||||||
|
|
||||||
// Notify owner that the service is dead
|
// Notify owner that the service is dead
|
||||||
onDisconnect()
|
onDisconnect()
|
|
@ -6,83 +6,13 @@
|
||||||
|
|
||||||
package org.serenityos.ladybird
|
package org.serenityos.ladybird
|
||||||
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Intent
|
|
||||||
import android.util.Log
|
|
||||||
import android.os.ParcelFileDescriptor
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.IBinder
|
|
||||||
import android.os.Message
|
import android.os.Message
|
||||||
import android.os.Messenger
|
|
||||||
import java.util.concurrent.Executors
|
|
||||||
|
|
||||||
const val MSG_SET_RESOURCE_ROOT = 1
|
class WebContentService : LadybirdServiceBase("WebContentService") {
|
||||||
const val MSG_TRANSFER_SOCKETS = 2
|
override fun handleServiceSpecificMessage(msg: Message): Boolean {
|
||||||
|
return false
|
||||||
class WebContentService : Service() {
|
|
||||||
private val TAG = "WebContentService"
|
|
||||||
|
|
||||||
private val threadPool = Executors.newCachedThreadPool()
|
|
||||||
private lateinit var resourceDir: String
|
|
||||||
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
Log.i(TAG, "Creating Service")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
Log.i(TAG, "Destroying Service")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
||||||
Log.i(TAG, "Start command received")
|
|
||||||
return super.onStartCommand(intent, flags, startId)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleTransferSockets(msg: Message) {
|
|
||||||
val bundle = msg.data
|
|
||||||
// FIXME: Handle garbage messages from wierd clients
|
|
||||||
val ipcSocket = bundle.getParcelable<ParcelFileDescriptor>("IPC_SOCKET")!!
|
|
||||||
val fdSocket = bundle.getParcelable<ParcelFileDescriptor>("FD_PASSING_SOCKET")!!
|
|
||||||
createThread(ipcSocket, fdSocket)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleSetResourceRoot(msg: Message) {
|
|
||||||
// FIXME: Handle this being already set, not being present, etc
|
|
||||||
resourceDir = msg.data.getString("PATH")!!
|
|
||||||
|
|
||||||
initNativeCode(resourceDir, TAG)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class IncomingHandler(
|
|
||||||
context: WebContentService,
|
|
||||||
private val owner: WebContentService = context
|
|
||||||
) : Handler() {
|
|
||||||
override fun handleMessage(msg: Message) {
|
|
||||||
when (msg.what) {
|
|
||||||
MSG_TRANSFER_SOCKETS -> this.owner.handleTransferSockets(msg)
|
|
||||||
MSG_SET_RESOURCE_ROOT -> this.owner.handleSetResourceRoot(msg)
|
|
||||||
else -> super.handleMessage(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(p0: Intent?): IBinder? {
|
|
||||||
// FIXME: Check the intent to make sure it's legit
|
|
||||||
return Messenger(IncomingHandler(this)).binder
|
|
||||||
}
|
|
||||||
|
|
||||||
private external fun nativeThreadLoop(ipcSocket: Int, fdPassingSocket: Int)
|
|
||||||
|
|
||||||
private fun createThread(ipcSocket: ParcelFileDescriptor, fdSocket: ParcelFileDescriptor) {
|
|
||||||
threadPool.execute {
|
|
||||||
nativeThreadLoop(ipcSocket.detachFd(), fdSocket.detachFd())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private external fun initNativeCode(resourceDir: String, tagName: String);
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
init {
|
init {
|
||||||
System.loadLibrary("webcontent")
|
System.loadLibrary("webcontent")
|
||||||
|
|
|
@ -51,7 +51,7 @@ class WebViewImplementation(private val view: WebView) {
|
||||||
|
|
||||||
// Functions called from native code
|
// Functions called from native code
|
||||||
fun bindWebContentService(ipcFd: Int, fdPassingFd: Int) {
|
fun bindWebContentService(ipcFd: Int, fdPassingFd: Int) {
|
||||||
val connector = WebContentServiceConnection(ipcFd, fdPassingFd, resourceDir)
|
val connector = LadybirdServiceConnection(ipcFd, fdPassingFd, resourceDir)
|
||||||
connector.onDisconnect = {
|
connector.onDisconnect = {
|
||||||
// FIXME: Notify impl that service is dead and might need restarted
|
// FIXME: Notify impl that service is dead and might need restarted
|
||||||
Log.e("WebContentView", "WebContent Died! :(")
|
Log.e("WebContentView", "WebContent Died! :(")
|
||||||
|
|
|
@ -55,7 +55,7 @@ else()
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
target_sources(webcontent PRIVATE
|
target_sources(webcontent PRIVATE
|
||||||
../Android/src/main/cpp/WebContentService.cpp
|
../Android/src/main/cpp/WebContentService.cpp
|
||||||
../Android/src/main/cpp/WebContentServiceJNI.cpp
|
../Android/src/main/cpp/LadybirdServiceBaseJNI.cpp
|
||||||
)
|
)
|
||||||
target_link_libraries(webcontent PRIVATE android)
|
target_link_libraries(webcontent PRIVATE android)
|
||||||
endif()
|
endif()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue