ipc/android: Add a blocking connect Java method, and small clean-ups.

This commit is contained in:
Ryan Pavlik 2020-11-06 11:29:51 -06:00 committed by Lubosz Sarnecki
parent 04318b4104
commit 3011e3c9f6
4 changed files with 102 additions and 35 deletions
src/xrt/ipc/android/src/main

View file

@ -5,7 +5,11 @@
SPDX-License-Identifier: BSL-1.0
-->
<application>
<service android:name=".MonadoService" android:enabled="true" android:exported="true" android:externalService="true">
<service
android:name=".MonadoService"
android:enabled="true"
android:exported="true"
android:externalService="true">
<intent-filter>
<action android:name="org.freedesktop.monado.CONNECT" />
</intent-filter>

View file

@ -21,6 +21,8 @@ import android.util.Log;
import androidx.annotation.Keep;
import org.freedesktop.monado.auxiliary.NativeCounterpart;
import java.io.IOException;
/**
@ -31,49 +33,103 @@ import java.io.IOException;
@Keep
public class Client implements ServiceConnection {
private static final String TAG = "monado-ipc-client";
/**
* Context provided by app.
* Used to block native until we have our side of the socket pair.
*/
private Context context;
private final Object connectSync = new Object();
/**
* Pointer to local IPC proxy: calling methods on it automatically transports arguments across binder IPC.
* <p>
* May be null!
*/
public IMonado monado;
/**
* "Our" side of the socket pair - the other side is sent to the server automatically on connection.
*/
public ParcelFileDescriptor fd;
@Keep
public IMonado monado = null;
/**
* Indicates that we tried to connect but failed.
* <p>
* Used to distinguish a "not yet fully connected" null monado member from a "tried and failed"
* null monado member.
*/
@Keep
public boolean failed = false;
/**
* Keep track of the ipc_client_android instance over on the native side.
*/
private final NativeCounterpart nativeCounterpart;
/**
* "Our" side of the socket pair - the other side is sent to the server automatically on connection.
*/
private ParcelFileDescriptor fd = null;
/**
* Context provided by app.
*/
private Context context;
/**
* Bind to the Monado IPC service - this asynchronously starts connecting (and launching the
* service if it's not already running)
* Constructor
*
* @param nativePointer the corresponding native object's pointer.
*/
@Keep
public Client(long nativePointer) {
this.nativeCounterpart = new NativeCounterpart(nativePointer);
this.nativeCounterpart.markAsUsedByNativeCode();
}
/**
* Let the native code notify us that it is no longer using this class.
*/
@Keep
public void markAsDiscardedByNative() {
nativeCounterpart.markAsDiscardedByNative(TAG);
}
/**
* Bind to the Monado IPC service, and block until it is fully connected.
* <p>
* The IPC client code on Android should load this class (from the right package), instantiate
* this class, and call this method.
* this class (retaining a reference to it!), and call this method.
*
* @param context_ Context to use to make the connection. (We get the application context
* from it.)
* @param packageName The package name containing the Monado runtime. The caller is guaranteed
* to know this because it had to load this class from that package.
* (Often "org.freedesktop.monado.openxr.out_of_process" for now, at least)
* @todo how to get the right package name here? Do we have to go so far as to re-enumerate ourselves?
* There's a define in xrt_config_android.h to use for this.
* @return the fd number - do not close! (dup if you want to be able to close it) Returns -1 if
* something went wrong.
* <p>
* Various builds, variants, etc. will have different package names, but we must specify the
* package name explicitly to avoid violating security restrictions.
*/
@Keep
public int blockingConnect(Context context_, String packageName) {
synchronized (connectSync) {
bind(context_, packageName);
try {
while (fd == null) {
connectSync.wait();
}
} catch (InterruptedException e) {
return -1;
}
}
return fd.getFd();
}
/**
* Bind to the Monado IPC service - this asynchronously starts connecting (and launching the
* service if it's not already running)
*
* @param context_ Context to use to make the connection. (We get the application context
* from it.)
* @param packageName The package name containing the Monado runtime. The caller is guaranteed
* to know this because it had to load this class from that package.
* There's a define in xrt_config_android.h to use for this.
* <p>
* Various builds, variants, etc. will have different package names, but we
* must specify the package name explicitly to avoid violating security
* restrictions.
*/
public void bind(Context context_, String packageName) {
context = context_.getApplicationContext();
if (context == null) {
@ -110,9 +166,10 @@ public class Client implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
monado = IMonado.Stub.asInterface(service);
ParcelFileDescriptor theirs;
ParcelFileDescriptor ours;
try {
ParcelFileDescriptor[] fds = ParcelFileDescriptor.createSocketPair();
fd = fds[0];
ours = fds[0];
theirs = fds[1];
} catch (IOException e) {
e.printStackTrace();
@ -126,6 +183,10 @@ public class Client implements ServiceConnection {
e.printStackTrace();
Log.e(TAG, "could not call IMonado.connect: " + e.toString());
handleFailure();
return;
}
synchronized (connectSync) {
fd = ours;
}
}
@ -137,6 +198,7 @@ public class Client implements ServiceConnection {
@Override
public void onServiceDisconnected(ComponentName name) {
monado = null;
//! @todo tell native that the world is crumbling, then close the fd here.
}
/*

View file

@ -15,18 +15,25 @@ import android.view.Surface;
import androidx.annotation.Keep;
import org.freedesktop.monado.ipc.IMonado.Stub;
/**
* Java implementation of the IMonado IPC interface.
*
* <p>
* (This is the server-side code.)
* <p>
* All this does is delegate calls to native JNI implementations
*/
@SuppressWarnings("JavaJniMissingFunction")
@Keep
public class MonadoImpl extends IMonado.Stub {
static {
// Load the shared library with the native parts of this class
// This is the service-lib target.
System.loadLibrary("monado-service");
}
public void connect(ParcelFileDescriptor parcelFileDescriptor) {
nativeAddClient(parcelFileDescriptor);
nativeAddClient(parcelFileDescriptor.detachFd());
}
public void passAppSurface(Surface surface) {
@ -36,13 +43,13 @@ public class MonadoImpl extends IMonado.Stub {
/**
* Native handling of receiving a surface: should convert it to an ANativeWindow then do stuff
* with it.
*
* <p>
* Ignore Android Studio complaining that this function is missing: it is not, it is just in a
* different module. See `src/xrt/targets/service-lib/lib.cpp` for the implementation.
* (Ignore the warning saying that file isn't included in the build: it is, Android Studio
* is just confused.)
*
* @param surface
* @param surface The surface to pass to native code
* @todo figure out a good way to make the MonadoImpl pointer a client ID
*/
private native void nativeAppSurface(Surface surface);
@ -50,24 +57,18 @@ 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 Android Studio complaining that this function is missing: it is not, it is just in a
* different module. See `src/xrt/targets/service-lib/lib.cpp` for the implementation.
* (Ignore the warning saying that file isn't included in the build: it is, Android Studio
* is just confused.)
*
* @param surface
* @param fd The incoming file descriptor: ownership is transferred to native code here.
* @todo figure out a good way to make the MonadoImpl pointer a client ID
*/
private native void nativeAddClient(ParcelFileDescriptor parcelFileDescriptor);
static {
// Load the shared library with the native parts of this class
// This is the service-lib target.
System.loadLibrary("monado-service");
}
private native void nativeAddClient(int fd);
}

View file

@ -18,7 +18,7 @@ import androidx.annotation.Nullable;
/**
* Minimal implementation of a Service.
*
* <p>
* This is needed so that the APK can expose the binder service implemented in MonadoImpl.
*/
public class MonadoService extends Service {