mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 17:57:35 +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>
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
#include "WebContentService.h"
|
||||
#include "LadybirdServiceBase.h"
|
||||
#include <AK/Atomic.h>
|
||||
#include <AK/Format.h>
|
||||
#include <Ladybird/Utilities.h>
|
||||
#include <jni.h>
|
||||
|
||||
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);
|
||||
auto ret = web_content_main(ipc_socket, fd_passing_socket);
|
||||
auto ret = service_main(ipc_socket, fd_passing_socket);
|
||||
if (ret.is_error()) {
|
||||
warnln("Runtime Error: {}", ret.release_error());
|
||||
} else {
|
||||
|
@ -23,7 +23,7 @@ Java_org_serenityos_ladybird_WebContentService_nativeThreadLoop(JNIEnv*, jobject
|
|||
}
|
||||
|
||||
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 };
|
||||
if (s_initialized_flag.exchange(true) == true) {
|
|
@ -4,7 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "WebContentService.h"
|
||||
#include "LadybirdServiceBase.h"
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <Ladybird/FontPlugin.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;
|
||||
|
||||
|
|
|
@ -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.ParcelFileDescriptor
|
||||
|
||||
class WebContentServiceConnection(
|
||||
class LadybirdServiceConnection(
|
||||
private var ipcFd: Int,
|
||||
private var fdPassingFd: Int,
|
||||
private var resourceDir: String
|
||||
) :
|
||||
ServiceConnection {
|
||||
var boundToWebContent: Boolean = false
|
||||
var boundToService: Boolean = false
|
||||
var onDisconnect: () -> Unit = {}
|
||||
private var webContentService: Messenger? = null
|
||||
private var service: Messenger? = null
|
||||
|
||||
override fun onServiceConnected(className: ComponentName, svc: IBinder) {
|
||||
// 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
|
||||
// service using a Messenger, so here we get a client-side
|
||||
// representation of that from the raw IBinder object.
|
||||
webContentService = Messenger(svc)
|
||||
boundToWebContent = true
|
||||
service = Messenger(svc)
|
||||
boundToService = true
|
||||
|
||||
val init = Message.obtain(null, MSG_SET_RESOURCE_ROOT)
|
||||
init.data.putString("PATH", resourceDir)
|
||||
webContentService!!.send(init)
|
||||
service!!.send(init)
|
||||
|
||||
val msg = Message.obtain(null, MSG_TRANSFER_SOCKETS)
|
||||
msg.data.putParcelable("IPC_SOCKET", ParcelFileDescriptor.adoptFd(ipcFd))
|
||||
msg.data.putParcelable("FD_PASSING_SOCKET", ParcelFileDescriptor.adoptFd(fdPassingFd))
|
||||
webContentService!!.send(msg)
|
||||
service!!.send(msg)
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(className: ComponentName) {
|
||||
// This is called when the connection with the service has been
|
||||
// unexpectedly disconnected; that is, its process crashed.
|
||||
webContentService = null
|
||||
boundToWebContent = false
|
||||
service = null
|
||||
boundToService = false
|
||||
|
||||
// Notify owner that the service is dead
|
||||
onDisconnect()
|
|
@ -6,83 +6,13 @@
|
|||
|
||||
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.Messenger
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
const val MSG_SET_RESOURCE_ROOT = 1
|
||||
const val MSG_TRANSFER_SOCKETS = 2
|
||||
|
||||
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")
|
||||
class WebContentService : LadybirdServiceBase("WebContentService") {
|
||||
override fun handleServiceSpecificMessage(msg: Message): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
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 {
|
||||
init {
|
||||
System.loadLibrary("webcontent")
|
||||
|
|
|
@ -51,7 +51,7 @@ class WebViewImplementation(private val view: WebView) {
|
|||
|
||||
// Functions called from native code
|
||||
fun bindWebContentService(ipcFd: Int, fdPassingFd: Int) {
|
||||
val connector = WebContentServiceConnection(ipcFd, fdPassingFd, resourceDir)
|
||||
val connector = LadybirdServiceConnection(ipcFd, fdPassingFd, resourceDir)
|
||||
connector.onDisconnect = {
|
||||
// FIXME: Notify impl that service is dead and might need restarted
|
||||
Log.e("WebContentView", "WebContent Died! :(")
|
||||
|
|
|
@ -55,7 +55,7 @@ else()
|
|||
if (ANDROID)
|
||||
target_sources(webcontent PRIVATE
|
||||
../Android/src/main/cpp/WebContentService.cpp
|
||||
../Android/src/main/cpp/WebContentServiceJNI.cpp
|
||||
../Android/src/main/cpp/LadybirdServiceBaseJNI.cpp
|
||||
)
|
||||
target_link_libraries(webcontent PRIVATE android)
|
||||
endif()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue