comp/android: Refine surface creation flow

Reuse MonadoView when "Display over other apps" is enabled. Move surface
creation logic to compositor for consistency. With this approach, compositor
implementer controls the way surface is created.
This commit is contained in:
Jarvis Huang 2022-10-04 10:58:50 +08:00 committed by Ryan Pavlik
parent cf7e44b710
commit 0d31791092
10 changed files with 57 additions and 106 deletions

View file

@ -0,0 +1 @@
Android: Refactor surface creation flow.

View file

@ -17,6 +17,7 @@
#include "wrap/android.content.h"
#include "wrap/android.hardware.display.h"
#include "wrap/android.provider.h"
#include "wrap/android.view.h"
#include "org.freedesktop.monado.auxiliary.hpp"
@ -24,6 +25,7 @@
using wrap::android::content::Context;
using wrap::android::hardware::display::DisplayManager;
using wrap::android::provider::Settings;
using wrap::android::view::Display;
using wrap::android::view::SurfaceHolder;
using wrap::android::view::WindowManager_LayoutParams;
@ -198,3 +200,10 @@ android_custom_surface_get_display_metrics(struct _JavaVM *vm,
return false;
}
}
bool
android_custom_surface_can_draw_overlays(struct _JavaVM *vm, void *context)
{
jni::init(vm);
return Settings::canDrawOverlays(Context{(jobject)context});
}

View file

@ -93,6 +93,9 @@ android_custom_surface_get_display_metrics(struct _JavaVM *vm,
void *activity,
struct xrt_android_display_metrics *out_metrics);
bool
android_custom_surface_can_draw_overlays(struct _JavaVM *vm, void *context);
#ifdef __cplusplus
}
#endif

View file

@ -89,7 +89,7 @@ _create_android_window(struct comp_window_android *cwa)
{
// 0 means default display
cwa->custom_surface =
android_custom_surface_async_start(android_globals_get_vm(), android_globals_get_activity(), 0);
android_custom_surface_async_start(android_globals_get_vm(), android_globals_get_context(), 0);
if (cwa->custom_surface == NULL) {
COMP_ERROR(cwa->base.base.c,
"comp_window_android_create_surface: could not "
@ -138,11 +138,14 @@ comp_window_android_init_swapchain(struct comp_target *ct, uint32_t width, uint3
if (android_globals_get_activity() != NULL) {
/* In process: Creating surface from activity */
window = _create_android_window(cwa);
} else if (android_custom_surface_can_draw_overlays(android_globals_get_vm(), android_globals_get_context())) {
/* Out of process: Create surface */
window = _create_android_window(cwa);
} else {
/* Out of process: Getting cached surface.
* This loop polls for a surface created by Client.java in blockingConnect.
* TODO: change java code to callback native code to notify Session lifecycle progress, instead of
* polling here
* TODO: change java code to callback native code to notify Session lifecycle progress, instead
* of polling here
*/
for (int i = 0; i < 100; i++) {
window = (struct ANativeWindow *)android_globals_get_window();
@ -152,6 +155,7 @@ comp_window_android_init_swapchain(struct comp_target *ct, uint32_t width, uint3
}
}
if (window == NULL) {
COMP_ERROR(cwa->base.base.c, "could not get ANativeWindow");
return false;

View file

@ -23,11 +23,6 @@ interface IMonado {
*/
void passAppSurface(in Surface surface);
/*!
* Asking service to create surface and attach it to the display matches given display id.
*/
boolean createSurface(int displayId, boolean focusable);
/*!
* Asking service whether it has the capbility to draw over other apps or not.
*/

View file

@ -22,7 +22,6 @@ import android.os.RemoteException;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.WindowManager;
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
@ -155,41 +154,26 @@ public class Client implements ServiceConnection {
// (ready ... synchronized ... visible ... focused)
new Thread(
() -> {
boolean surfaceCreated = false;
Activity activity = null;
if (context_ instanceof Activity) {
activity = (Activity) context_;
}
try {
// Determine whether runtime or client should create surface
if (monado.canDrawOverOtherApps()) {
WindowManager wm =
(WindowManager)
context_.getSystemService(
Context.WINDOW_SERVICE);
surfaceCreated =
monado.createSurface(
wm.getDefaultDisplay().getDisplayId(), false);
} else {
if (activity != null) {
Surface surface = attachViewAndGetSurface(activity);
surfaceCreated = (surface != null);
if (surfaceCreated) {
monado.passAppSurface(surface);
}
if (!monado.canDrawOverOtherApps() && activity != null) {
Surface surface = attachViewAndGetSurface(activity);
if (surface == null) {
Log.e(TAG, "Failed to create surface");
handleFailure();
return;
}
monado.passAppSurface(surface);
}
} catch (RemoteException e) {
e.printStackTrace();
}
if (!surfaceCreated) {
Log.e(TAG, "Failed to create surface");
handleFailure();
return;
}
if (activity != null) {
systemUiController =
new SystemUiController(activity.getWindow().getDecorView());

View file

@ -9,11 +9,12 @@
package org.freedesktop.monado.ipc;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
@ -40,30 +41,15 @@ public class MonadoImpl extends IMonado.Stub {
System.loadLibrary("monado-service");
}
private SurfaceManager surfaceManager;
private final Context context;
public MonadoImpl(@NonNull SurfaceManager surfaceManager) {
this.surfaceManager = surfaceManager;
this.surfaceManager.setCallback(
new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
Log.i(TAG, "surfaceCreated");
passAppSurface(holder.getSurface());
}
@Override
public void surfaceChanged(
@NonNull SurfaceHolder holder, int format, int width, int height) {}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {}
});
public MonadoImpl(@NonNull Context context) {
this.context = context.getApplicationContext();
nativeStartServer(this.context);
}
@Override
public void connect(@NonNull ParcelFileDescriptor parcelFileDescriptor) throws RemoteException {
nativeStartServer();
int fd = parcelFileDescriptor.getFd();
Log.i(TAG, "connect: given fd " + fd);
if (nativeAddClient(fd) != 0) {
@ -90,19 +76,12 @@ public class MonadoImpl extends IMonado.Stub {
return;
}
nativeAppSurface(surface);
nativeStartServer();
}
@Override
public boolean createSurface(int displayId, boolean focusable) {
Log.i(TAG, "createSurface");
return surfaceManager.createSurfaceOnDisplay(displayId, focusable);
}
@Override
public boolean canDrawOverOtherApps() {
Log.i(TAG, "canDrawOverOtherApps");
return surfaceManager.canDrawOverlays();
return Settings.canDrawOverlays(context);
}
public void shutdown() {
@ -110,9 +89,17 @@ public class MonadoImpl extends IMonado.Stub {
nativeShutdownServer();
}
/** Native method that starts server. */
/**
* Native method that starts server.
*
* <p>This is essentially the entry point for the monado service on Android.
*
* <p>
*
* @param context Context object.
*/
@SuppressWarnings("JavaJniMissingFunction")
private native void nativeStartServer();
private native void nativeStartServer(@NonNull Context context);
/**
* Native handling of receiving a surface: should convert it to an ANativeWindow then do stuff
@ -131,10 +118,6 @@ public class MonadoImpl extends IMonado.Stub {
* Native handling of receiving an FD for a new client: the FD should be used to start up the
* rest of the native IPC code on that socket.
*
* <p>This is essentially the entry point for the monado service on Android: if it's already
* running, this will be called in it. If it's not already running, a process will be created,
* and this will be the first native code executed in that process.
*
* <p>Ignore warnings that this function is missing: it is not, it is just in a different
* module. See `src/xrt/targets/service-lib/service_target.cpp` for the implementation.
*

View file

@ -32,19 +32,15 @@ class MonadoService : Service(), Watchdog.ShutdownListener {
@Inject lateinit var serviceNotification: IServiceNotification
private lateinit var surfaceManager: SurfaceManager
override fun onCreate() {
super.onCreate()
surfaceManager = SurfaceManager(this)
binder = MonadoImpl(surfaceManager)
binder = MonadoImpl(this)
watchdog =
Watchdog(
// If the surface comes from client, just stop the service when client disconnected
// because the surface belongs to the client.
if (surfaceManager.canDrawOverlays()) BuildConfig.WATCHDOG_TIMEOUT_MILLISECONDS
else 0,
if (binder.canDrawOverOtherApps()) BuildConfig.WATCHDOG_TIMEOUT_MILLISECONDS else 0,
this
)
watchdog.startMonitor()
@ -61,7 +57,6 @@ class MonadoService : Service(), Watchdog.ShutdownListener {
binder.shutdown()
watchdog.stopMonitor()
surfaceManager.destroySurface()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {

View file

@ -8,25 +8,9 @@
package org.freedesktop.monado.openxr_runtime;
import android.app.Application;
import android.content.Context;
import androidx.annotation.NonNull;
import dagger.hilt.android.HiltAndroidApp;
/** Subclass required for Hilt usage. */
@HiltAndroidApp
public class MonadoOpenXrApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (!BuildConfig.inProcess) {
System.loadLibrary("monado-service");
nativeStoreContext(getApplicationContext());
}
}
private native void nativeStoreContext(@NonNull Context context);
}
public class MonadoOpenXrApplication extends Application {}

View file

@ -133,13 +133,20 @@ private:
};
} // namespace
extern "C" void
Java_org_freedesktop_monado_ipc_MonadoImpl_nativeStartServer(JNIEnv *env, jobject thiz)
extern "C" JNIEXPORT void JNICALL
Java_org_freedesktop_monado_ipc_MonadoImpl_nativeStartServer(JNIEnv *env, jobject thiz, jobject context)
{
JavaVM *jvm = nullptr;
jint result = env->GetJavaVM(&jvm);
assert(result == JNI_OK);
assert(jvm);
jni::init(env);
jni::Object monadoImpl(thiz);
U_LOG_D("service: Called nativeStartServer");
android_globals_store_vm_and_context(jvm, context);
IpcServerHelper::instance().startServer();
}
@ -174,17 +181,3 @@ Java_org_freedesktop_monado_ipc_MonadoImpl_nativeShutdownServer(JNIEnv *env, job
return IpcServerHelper::instance().shutdownServer();
}
extern "C" JNIEXPORT void JNICALL
Java_org_freedesktop_monado_openxr_1runtime_MonadoOpenXrApplication_nativeStoreContext(JNIEnv *env,
jobject thiz,
jobject context)
{
JavaVM *jvm = nullptr;
jint result = env->GetJavaVM(&jvm);
assert(result == JNI_OK);
assert(jvm);
jni::init(env);
android_globals_store_vm_and_context(jvm, context);
}