1
Fork 0
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:
Andrew Kaster 2023-09-15 15:09:01 -06:00 committed by Andrew Kaster
parent 315ad2d391
commit da8f450335
8 changed files with 117 additions and 91 deletions

View file

@ -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);

View file

@ -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) {

View file

@ -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;

View file

@ -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)
}
}
}
}
}
}

View file

@ -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()

View file

@ -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")

View file

@ -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! :(")

View file

@ -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()