make Looper, Handler, and MessageQueue work properly

this for example makes Unity apps not steal the main thread,
hanging Gtk.
This commit is contained in:
Mis012
2023-07-25 14:26:29 +02:00
parent 7ac5587fca
commit 08998b0076
15 changed files with 997 additions and 158 deletions

View File

@@ -59,6 +59,7 @@ libandroid_so = shared_library('android', [
libtranslationlayer_so = shared_library('translation_layer_main', [ libtranslationlayer_so = shared_library('translation_layer_main', [
'src/api-impl-jni/egl/com_google_android_gles_jni_EGLImpl.c', 'src/api-impl-jni/egl/com_google_android_gles_jni_EGLImpl.c',
'src/api-impl-jni/android_os_Environment.c', 'src/api-impl-jni/android_os_Environment.c',
'src/api-impl-jni/android_os_MessageQueue.c',
'src/api-impl-jni/android_os_SystemClock.c', 'src/api-impl-jni/android_os_SystemClock.c',
'src/api-impl-jni/android_view_Window.c', 'src/api-impl-jni/android_view_Window.c',
'src/api-impl-jni/util.c', 'src/api-impl-jni/util.c',

View File

@@ -0,0 +1,62 @@
#include "defines.h"
#include "util.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
/* TODO put these in a header */
typedef void ALooper;
ALooper * ALooper_prepare(void);
void ALooper_wake(ALooper *looper);
bool ALooper_isPolling(ALooper *looper);
int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);
struct native_message_queue {
ALooper *looper;
bool in_callback;
};
JNIEXPORT jlong JNICALL Java_android_os_MessageQueue_nativeInit(JNIEnv *env, jclass this)
{
struct native_message_queue *message_queue = malloc(sizeof(struct native_message_queue));
message_queue->in_callback = false;
message_queue->looper = ALooper_prepare();
return _INTPTR(message_queue);
}
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeDestroy(JNIEnv *env, jclass this, jlong ptr)
{
struct native_message_queue *message_queue = _PTR(ptr);
free(message_queue);
}
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativePollOnce(JNIEnv *env, jclass this, jlong ptr, jint timeout_millis)
{
struct native_message_queue *message_queue = _PTR(ptr);
// printf("Java_android_os_MessageQueue_nativePollOnce: entry (timeout: %d)\n", timeout_millis);
message_queue->in_callback = true;
ALooper_pollOnce(timeout_millis, NULL, NULL, NULL);
message_queue->in_callback = false;
// printf("Java_android_os_MessageQueue_nativePollOnce: exit\n");
/* TODO: what's with the exception stuff */
}
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeWake(JNIEnv *env, jclass this, jlong ptr)
{
struct native_message_queue *message_queue = _PTR(ptr);
ALooper_wake(message_queue->looper);
}
JNIEXPORT jboolean JNICALL Java_android_os_MessageQueue_nativeIsIdling(JNIEnv *env, jclass this, jlong ptr)
{
struct native_message_queue *message_queue = _PTR(ptr);
return ALooper_isPolling(message_queue->looper);
}

View File

@@ -1,3 +1,6 @@
#include <time.h>
#include <math.h>
#include "generated_headers/android_os_SystemClock.h" #include "generated_headers/android_os_SystemClock.h"
JNIEXPORT jlong JNICALL Java_android_os_SystemClock_elapsedRealtime(JNIEnv *env, jclass this) JNIEXPORT jlong JNICALL Java_android_os_SystemClock_elapsedRealtime(JNIEnv *env, jclass this)
@@ -5,3 +8,10 @@ JNIEXPORT jlong JNICALL Java_android_os_SystemClock_elapsedRealtime(JNIEnv *env,
printf("FIXME: Java_android_os_SystemClock_elapsedRealtime: returning 0\n"); printf("FIXME: Java_android_os_SystemClock_elapsedRealtime: returning 0\n");
return 0; // FIXME return 0; // FIXME
} }
JNIEXPORT jlong JNICALL Java_android_os_SystemClock_uptimeMillis(JNIEnv *env, jclass this)
{
struct timespec t;
clock_gettime(CLOCK_REALTIME, &t);
return t.tv_sec * 1000 + lround(t.tv_nsec / 1e6);
}

View File

@@ -10,42 +10,42 @@ extern "C" {
/* /*
* Class: android_os_MessageQueue * Class: android_os_MessageQueue
* Method: nativeInit * Method: nativeInit
* Signature: ()I * Signature: ()J
*/ */
JNIEXPORT jint JNICALL Java_android_os_MessageQueue_nativeInit JNIEXPORT jlong JNICALL Java_android_os_MessageQueue_nativeInit
(JNIEnv *, jclass); (JNIEnv *, jclass);
/* /*
* Class: android_os_MessageQueue * Class: android_os_MessageQueue
* Method: nativeDestroy * Method: nativeDestroy
* Signature: (I)V * Signature: (J)V
*/ */
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeDestroy JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeDestroy
(JNIEnv *, jclass, jint); (JNIEnv *, jclass, jlong);
/* /*
* Class: android_os_MessageQueue * Class: android_os_MessageQueue
* Method: nativePollOnce * Method: nativePollOnce
* Signature: (II)V * Signature: (JI)V
*/ */
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativePollOnce JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativePollOnce
(JNIEnv *, jclass, jint, jint); (JNIEnv *, jclass, jlong, jint);
/* /*
* Class: android_os_MessageQueue * Class: android_os_MessageQueue
* Method: nativeWake * Method: nativeWake
* Signature: (I)V * Signature: (J)V
*/ */
JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeWake JNIEXPORT void JNICALL Java_android_os_MessageQueue_nativeWake
(JNIEnv *, jclass, jint); (JNIEnv *, jclass, jlong);
/* /*
* Class: android_os_MessageQueue * Class: android_os_MessageQueue
* Method: nativeIsIdling * Method: nativeIsIdling
* Signature: (I)Z * Signature: (J)Z
*/ */
JNIEXPORT jboolean JNICALL Java_android_os_MessageQueue_nativeIsIdling JNIEXPORT jboolean JNICALL Java_android_os_MessageQueue_nativeIsIdling
(JNIEnv *, jclass, jint); (JNIEnv *, jclass, jlong);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -15,6 +15,14 @@ extern "C" {
JNIEXPORT jboolean JNICALL Java_android_os_SystemClock_setCurrentTimeMillis JNIEXPORT jboolean JNICALL Java_android_os_SystemClock_setCurrentTimeMillis
(JNIEnv *, jclass, jlong); (JNIEnv *, jclass, jlong);
/*
* Class: android_os_SystemClock
* Method: uptimeMillis
* Signature: ()J
*/
JNIEXPORT jlong JNICALL Java_android_os_SystemClock_uptimeMillis
(JNIEnv *, jclass);
/* /*
* Class: android_os_SystemClock * Class: android_os_SystemClock
* Method: elapsedRealtime * Method: elapsedRealtime

View File

@@ -6,6 +6,8 @@ import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.XmlResourceParser; import android.content.res.XmlResourceParser;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -102,6 +104,12 @@ public class Activity extends Context {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
System.out.println("- onCreate - yay!"); System.out.println("- onCreate - yay!");
/* TODO: this probably belongs elsewhere, but this is our entry point for better or worse */
Looper looper = Looper.myLooper();
if(looper == null) {
Looper.prepareMainLooper();
}
return; return;
} }
@@ -212,7 +220,11 @@ public class Activity extends Context {
} }
public final void runOnUiThread(Runnable action) { public final void runOnUiThread(Runnable action) {
action.run(); // FIXME: running synchronously for now if(Looper.myLooper() == Looper.getMainLooper()) {
action.run();
} else {
new Handler(Looper.getMainLooper()).post(action);
}
} }
protected void onActivityResult(int requestCode, int resultCode, Intent data) {} protected void onActivityResult(int requestCode, int resultCode, Intent data) {}

View File

@@ -33,6 +33,7 @@ import android.view.WindowManager;
import android.view.WindowManagerImpl; import android.view.WindowManagerImpl;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
public class Context extends Object { public class Context extends Object {
@@ -138,8 +139,14 @@ public class Context extends Object {
} }
public Looper getMainLooper() { public Looper getMainLooper() {
System.out.println("returning the main Looper, most definitely doing just that!"); /* TODO: this is not what AOSP does, which could be a problem */
return new Looper(); Looper looper = Looper.myLooper();
if(looper == null) {
Looper.prepare();
looper = Looper.myLooper();
}
return looper;
} }
public String getPackageName() { public String getPackageName() {
@@ -251,9 +258,12 @@ public class Context extends Object {
return new ComponentName("", ""); return new ComponentName("", "");
} }
// FIXME - it should be *trivial* to do actually implement this // TODO: do these both work? make them look more alike
public FileInputStream openFileInput(String name) { public FileInputStream openFileInput(String name) throws FileNotFoundException {
return null; System.out.println("openFileInput called for: '" + name + "'");
File file = new File(getFilesDir(), name);
return new FileInputStream(file);
} }
public FileOutputStream openFileOutput(String name, int mode) throws java.io.FileNotFoundException { public FileOutputStream openFileOutput(String name, int mode) throws java.io.FileNotFoundException {

View File

@@ -738,6 +738,7 @@ public final class Bundle implements Cloneable {
*/ */
public boolean getBoolean(String key, boolean defaultValue) { public boolean getBoolean(String key, boolean defaultValue) {
Object o = mMap.get(key); Object o = mMap.get(key);
System.out.println("bundle.getBoolean(" + key + ", " + defaultValue + ") called");
if (o == null) { if (o == null) {
return defaultValue; return defaultValue;
} }

View File

@@ -225,7 +225,7 @@ public class Handler {
*/ */
public Handler(Looper looper, Callback callback, boolean async) { public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper; mLooper = looper;
mQueue = null /*looper.mQueue*/; mQueue = looper.mQueue;
mCallback = callback; mCallback = callback;
mAsynchronous = async; mAsynchronous = async;
} }
@@ -316,9 +316,7 @@ public class Handler {
* looper processing the message queue is exiting. * looper processing the message queue is exiting.
*/ */
public final boolean post(Runnable r) { public final boolean post(Runnable r) {
// return sendMessageDelayed(getPostMessage(r), 0); return sendMessageDelayed(getPostMessage(r), 0);
r.run();
return true;
} }
/** /**
@@ -571,29 +569,14 @@ public class Handler {
* occurs then the message will be dropped. * occurs then the message will be dropped.
*/ */
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
/* MessageQueue queue = mQueue; MessageQueue queue = mQueue;
if (queue == null) { if (queue == null) {
RuntimeException e = new RuntimeException( RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue"); this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e); Log.w("Looper", e.getMessage(), e);
return false; return false;
}
return enqueueMessage(queue, msg, uptimeMillis);*/
if (mCallback != null) {
// System.out.println("Handler.sendMessageAtTime: directly calling mCallback.handleMessage)");
if (msg.callback != null) {
msg.callback.run();
}
return mCallback.handleMessage(msg);
} else {
// System.out.println("Handler.sendMessageAtTime: not directly calling mCallback.handleMessage - mCallback is null)");
/* do this in this case as well?
if(msg.callback != null) {
msg.callback.run();
}
*/
return true; // false?
} }
return enqueueMessage(queue, msg, uptimeMillis);
} }
/** /**
@@ -609,15 +592,14 @@ public class Handler {
* looper processing the message queue is exiting. * looper processing the message queue is exiting.
*/ */
public final boolean sendMessageAtFrontOfQueue(Message msg) { public final boolean sendMessageAtFrontOfQueue(Message msg) {
/*MessageQueue queue = mQueue; MessageQueue queue = mQueue;
if (queue == null) { if (queue == null) {
RuntimeException e = new RuntimeException( RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue"); this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e); Log.w("Looper", e.getMessage(), e);
return false; return false;
} }
return enqueueMessage(queue, msg, 0);*/ return enqueueMessage(queue, msg, 0);
return true;
} }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

View File

@@ -1,19 +1,149 @@
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os; package android.os;
/**
* Handy class for starting a new thread that has a looper. The looper can then be
* used to create handler classes. Note that start() must still be called.
*/
public class HandlerThread extends Thread { public class HandlerThread extends Thread {
String name; int mPriority;
int mTid = -1;
public HandlerThread() {} Looper mLooper;
public HandlerThread(String name) { public HandlerThread(String name) {
this.name = name; super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
} }
public void start() { /**
// if(name.equals("FlurryAgent")) { return; } * Constructs a HandlerThread.
* @param name
* @param priority The priority to run the thread at. The value supplied must be from
* {@link android.os.Process} and not from java.lang.Thread.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
} }
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() { public Looper getLooper() {
return new Looper(); if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* Quits the handler thread's looper.
* <p>
* Causes the handler thread's looper to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*
* @see #quitSafely
*/
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
/**
* Quits the handler thread's looper safely.
* <p>
* Causes the handler thread's looper to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* Pending delayed messages with due times in the future will not be delivered.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p>
* If the thread has not been started or has finished (that is if
* {@link #getLooper} returns null), then false is returned.
* Otherwise the looper is asked to quit and true is returned.
* </p>
*
* @return True if the looper looper has been asked to quit or false if the
* thread had not yet started running.
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid().
*/
public int getThreadId() {
return mTid;
} }
} }

View File

@@ -53,13 +53,6 @@ import android.util.Printer;
public final class Looper { public final class Looper {
private static final String TAG = "Looper"; private static final String TAG = "Looper";
// FIXME
public Looper() {
mQueue = null;
mThread = Thread.currentThread();
System.out.println("making a fake Looper object, let's hope noone tries to actually use it");
}
// sThreadLocal.get() will return null unless you've called prepare(). // sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class private static Looper sMainLooper; // guarded by Looper.class
@@ -84,7 +77,7 @@ public final class Looper {
if (sThreadLocal.get() != null) { if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread"); throw new RuntimeException("Only one Looper may be created per thread");
} }
sThreadLocal.set(new Looper(/*quitAllowed*/)); sThreadLocal.set(new Looper(quitAllowed));
} }
/** /**
@@ -107,10 +100,9 @@ public final class Looper {
* Returns the application's main looper, which lives in the main thread of the application. * Returns the application's main looper, which lives in the main thread of the application.
*/ */
public static Looper getMainLooper() { public static Looper getMainLooper() {
return new Looper(); synchronized (Looper.class) {
/* synchronized (Looper.class) { return sMainLooper;
return sMainLooper; }
}*/
} }
/** /**
@@ -118,51 +110,46 @@ public final class Looper {
* {@link #quit()} to end the loop. * {@link #quit()} to end the loop.
*/ */
public static void loop() { public static void loop() {
System.out.println("oops, Looper.loop called... and we don't implement that"); final Looper me = myLooper();
/*final Looper me = myLooper();
if (me == null) { if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
} }
final MessageQueue queue = me.mQueue; final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process, // Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is. // and keep track of what that identity token actually is.
// Binder.clearCallingIdentity(); // Binder.clearCallingIdentity();
// final long ident = Binder.clearCallingIdentity(); // final long ident = Binder.clearCallingIdentity();
for (;;) { for (;;) {
Message msg = queue.next(); // might block Message msg = queue.next(); // might block
if (msg == null) { if (msg == null) {
// No message indicates that the message queue is quitting. // No message indicates that the message queue is quitting.
return; return;
} }
// This must be in a local variable, in case a UI event sets the logger // This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging; Printer logging = me.mLogging;
if (logging != null) { if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " + logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what); msg.callback + ": " + msg.what);
} }
msg.target.dispatchMessage(msg); msg.target.dispatchMessage(msg);
if (logging != null) { if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
} }
// Make sure that during the course of dispatching the // Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted. // identity of the thread wasn't corrupted.
// final long newIdent = Binder.clearCallingIdentity(); /* final long newIdent = Binder.clearCallingIdentity();
/* if (ident != newIdent) { if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x" Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what);
+ Long.toHexString(ident) + " to 0x" }
+ Long.toHexString(newIdent) + " while dispatching to " */
+ msg.target.getClass().getName() + " " msg.recycle();
+ msg.callback + " what=" + msg.what); }
}* /
msg.recycle();
}*/
} }
/** /**
@@ -170,11 +157,7 @@ public final class Looper {
* null if the calling thread is not associated with a Looper. * null if the calling thread is not associated with a Looper.
*/ */
public static Looper myLooper() { public static Looper myLooper() {
return new Looper(); return sThreadLocal.get();
/* if(sThreadLocal.get() == null) {
prepare(false);
}
return sThreadLocal.get();*/
} }
/** /**
@@ -293,8 +276,7 @@ public final class Looper {
* Return the Thread associated with this Looper. * Return the Thread associated with this Looper.
*/ */
public Thread getThread() { public Thread getThread() {
return Thread.currentThread(); // ugly hack return mThread;
// return mThread;
} }
/** /**

View File

@@ -34,7 +34,7 @@ public final class MessageQueue {
private final boolean mQuitAllowed; private final boolean mQuitAllowed;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private int mPtr; // used by native code private long mPtr; // used by native code
Message mMessages; Message mMessages;
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
@@ -48,11 +48,11 @@ public final class MessageQueue {
// Barriers are indicated by messages with a null target whose arg1 field carries the token. // Barriers are indicated by messages with a null target whose arg1 field carries the token.
private int mNextBarrierToken; private int mNextBarrierToken;
private native static int nativeInit(); private native static long nativeInit();
private native static void nativeDestroy(int ptr); private native static void nativeDestroy(long ptr);
private native static void nativePollOnce(int ptr, int timeoutMillis); private native static void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(int ptr); private native static void nativeWake(long ptr);
private native static boolean nativeIsIdling(int ptr); private native static boolean nativeIsIdling(long ptr);
/** /**
* Callback interface for discovering when a thread is going to block * Callback interface for discovering when a thread is going to block

View File

@@ -105,28 +105,25 @@ public final class SystemClock {
* @param ms to sleep before returning, in milliseconds of uptime. * @param ms to sleep before returning, in milliseconds of uptime.
*/ */
public static void sleep(long ms) { public static void sleep(long ms) {
System.out.println("!!! sleep(...) doesn't work, need to implement uptimeMillis"); long start = uptimeMillis();
/* long duration = ms;
long start = uptimeMillis(); boolean interrupted = false;
long duration = ms; do {
boolean interrupted = false; try {
do { Thread.sleep(duration);
try { } catch (InterruptedException e) {
Thread.sleep(duration); interrupted = true;
} }
catch (InterruptedException e) { duration = start + ms - uptimeMillis();
interrupted = true; } while (duration > 0);
}
duration = start + ms - uptimeMillis(); if (interrupted) {
} while (duration > 0); // Important: we don't want to quietly eat an interrupt() event,
// so we make sure to re-interrupt the thread so that the next
if (interrupted) { // call to Thread.sleep() or Object.wait() will be interrupted.
// Important: we don't want to quietly eat an interrupt() event, Thread.currentThread().interrupt();
// so we make sure to re-interrupt the thread so that the next }
// call to Thread.sleep() or Object.wait() will be interrupted. }
Thread.currentThread().interrupt();
}
*/}
/** /**
* Sets the current wall time, in milliseconds. Requires the calling * Sets the current wall time, in milliseconds. Requires the calling
@@ -141,9 +138,7 @@ public final class SystemClock {
* *
* @return milliseconds of non-sleep uptime since boot. * @return milliseconds of non-sleep uptime since boot.
*/ */
/*native */ public static long uptimeMillis() { native public static long uptimeMillis();
return 654000; // FIXME
}
/** /**
* Returns milliseconds since boot, including time spent in sleep. * Returns milliseconds since boot, including time spent in sleep.

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdio.h> #include <stdio.h>
@@ -37,7 +38,7 @@ void ALooper_release(ALooper* looper) {
_ZNK7android7RefBase9decStrongEPKv(looper, (void*)ALooper_acquire); _ZNK7android7RefBase9decStrongEPKv(looper, (void*)ALooper_acquire);
} }
int _ZN7android6Looper7pollAllEiPiS1_PPv(ALooper *this, int timeoutMillis, int* outFd, int* outEvents, void** outData); int _ZN7android6Looper7pollAllEiPiS1_PPv(ALooper *this, int timeoutMillis, int *outFd, int *outEvents, void **outData);
int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData)
{ {
ALooper *looper = ALooper_forThread(); ALooper *looper = ALooper_forThread();
@@ -49,12 +50,34 @@ int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outDat
return _ZN7android6Looper7pollAllEiPiS1_PPv(looper, timeoutMillis, outFd, outEvents, outData); return _ZN7android6Looper7pollAllEiPiS1_PPv(looper, timeoutMillis, outFd, outEvents, outData);
} }
int _ZN7android6Looper5addFdEiiiPFiiiPvES1_(ALooper *this, int fd, int ident, int events, Looper_callbackFunc callback, void* data); int _ZN7android6Looper8pollOnceEiPiS1_PPv(ALooper *this, int timeoutMillis, int *outFd, int *outEvents, void **outData);
int ALooper_pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData)
{
ALooper *looper = ALooper_forThread();
if(!looper) {
fprintf(stderr, "ALooper_pollAll: ALooper_forThread returned NULL\n");
return 0;
}
return _ZN7android6Looper8pollOnceEiPiS1_PPv(looper, timeoutMillis, outFd, outEvents, outData);
}
int _ZN7android6Looper5addFdEiiiPFiiiPvES1_(ALooper *this, int fd, int ident, int events, Looper_callbackFunc callback, void *data);
int ALooper_addFd(ALooper* looper, int fd, int ident, int events, Looper_callbackFunc callback, void* data) int ALooper_addFd(ALooper* looper, int fd, int ident, int events, Looper_callbackFunc callback, void* data)
{ {
return _ZN7android6Looper5addFdEiiiPFiiiPvES1_(looper, fd, ident, events, callback, data); return _ZN7android6Looper5addFdEiiiPFiiiPvES1_(looper, fd, ident, events, callback, data);
} }
void _ZN7android6Looper4wakeEv(ALooper *this); void _ZN7android6Looper4wakeEv(ALooper *this);
void ALooper_wake(ALooper* looper) { void ALooper_wake(ALooper *looper)
_ZN7android6Looper4wakeEv(looper); {
_ZN7android6Looper4wakeEv(looper);
}
/* this is not part of the android API, but we use it internally */
bool _ZNK7android6Looper9isPollingEv(ALooper *this);
bool ALooper_isPolling(ALooper *looper)
{
return _ZNK7android6Looper9isPollingEv(looper);
} }