Handle user message ID 0
Pass RS runtime errors back to java.
throw exceptions for runtime errors.

Change-Id: Ifcf16cbbf9b98137971dced5076f8a5563eb016c
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 0c9a4ed..b3774d4 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -62,7 +62,9 @@
     native int  nDeviceCreate();
     native void nDeviceDestroy(int dev);
     native void nDeviceSetConfig(int dev, int param, int value);
-    native int  nContextGetMessage(int con, int[] data, boolean wait);
+    native void nContextGetUserMessage(int con, int[] data);
+    native String nContextGetErrorMessage(int con);
+    native int  nContextPeekMessage(int con, int[] subID, boolean wait);
     native void nContextInitToClient(int con);
     native void nContextDeinitToClient(int con);
 
@@ -582,11 +584,20 @@
     public static class RSMessage implements Runnable {
         protected int[] mData;
         protected int mID;
+        protected int mLength;
         public void run() {
         }
     }
     public RSMessage mMessageCallback = null;
 
+    public static class RSAsyncError implements Runnable {
+        protected String mErrorMessage;
+        protected int mErrorNum;
+        public void run() {
+        }
+    }
+    public RSAsyncError mErrorCallback = null;
+
     public enum Priority {
         LOW (5),     //ANDROID_PRIORITY_BACKGROUND + 5
         NORMAL (-4);  //ANDROID_PRIORITY_DISPLAY
@@ -611,6 +622,13 @@
     protected static class MessageThread extends Thread {
         RenderScript mRS;
         boolean mRun = true;
+        int[] auxData = new int[2];
+
+        public static final int RS_MESSAGE_TO_CLIENT_NONE = 0;
+        public static final int RS_MESSAGE_TO_CLIENT_EXCEPTION = 1;
+        public static final int RS_MESSAGE_TO_CLIENT_RESIZE = 2;
+        public static final int RS_MESSAGE_TO_CLIENT_ERROR = 3;
+        public static final int RS_MESSAGE_TO_CLIENT_USER = 4;
 
         MessageThread(RenderScript rs) {
             super("RSMessageThread");
@@ -625,28 +643,47 @@
             mRS.nContextInitToClient(mRS.mContext);
             while(mRun) {
                 rbuf[0] = 0;
-                int msg = mRS.nContextGetMessage(mRS.mContext, rbuf, true);
-                if ((msg == 0)) {
-                    // Can happen for two reasons
-                    if (rbuf[0] > 0 && mRun) {
-                        // 1: Buffer needs to be enlarged.
-                        rbuf = new int[rbuf[0] + 2];
+                int msg = mRS.nContextPeekMessage(mRS.mContext, auxData, true);
+                int size = auxData[1];
+                int subID = auxData[0];
+
+                if (msg == RS_MESSAGE_TO_CLIENT_USER) {
+                    if ((size>>2) >= rbuf.length) {
+                        rbuf = new int[(size + 3) >> 2];
+                    }
+                    mRS.nContextGetUserMessage(mRS.mContext, rbuf);
+
+                    if(mRS.mMessageCallback != null) {
+                        mRS.mMessageCallback.mData = rbuf;
+                        mRS.mMessageCallback.mID = subID;
+                        mRS.mMessageCallback.mLength = size;
+                        mRS.mMessageCallback.run();
                     } else {
-                        // 2: teardown.
-                        // But we want to avoid starving other threads during
-                        // teardown by yielding until the next line in the destructor
-                        // can execute to set mRun = false
-                        try {
-                            sleep(1, 0);
-                        } catch(InterruptedException e) {
-                        }
+                        throw new RSInvalidStateException("Received a message from the script with no message handler installed.");
                     }
                     continue;
                 }
-                if(mRS.mMessageCallback != null) {
-                    mRS.mMessageCallback.mData = rbuf;
-                    mRS.mMessageCallback.mID = msg;
-                    mRS.mMessageCallback.run();
+
+                if (msg == RS_MESSAGE_TO_CLIENT_ERROR) {
+                    String e = mRS.nContextGetErrorMessage(mRS.mContext);
+
+                    if(mRS.mErrorCallback != null) {
+                        mRS.mErrorCallback.mErrorMessage = e;
+                        mRS.mErrorCallback.mErrorNum = subID;
+                        mRS.mErrorCallback.run();
+                    } else {
+                        //throw new RSRuntimeException("Received error num " + subID + ", details: " + e);
+                    }
+                    continue;
+                }
+
+                // 2: teardown.
+                // But we want to avoid starving other threads during
+                // teardown by yielding until the next line in the destructor
+                // can execute to set mRun = false
+                try {
+                    sleep(1, 0);
+                } catch(InterruptedException e) {
                 }
             }
             Log.d(LOG_TAG, "MessageThread exiting.");
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 77cbc10..dd84848 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -223,19 +223,48 @@
     rsContextResume(con);
 }
 
-static jint
-nContextGetMessage(JNIEnv *_env, jobject _this, RsContext con, jintArray data, jboolean wait)
+
+static jstring
+nContextGetErrorMessage(JNIEnv *_env, jobject _this, RsContext con)
+{
+    LOG_API("nContextGetErrorMessage, con(%p)", con);
+    char buf[1024];
+
+    size_t receiveLen;
+    uint32_t subID;
+    int id = rsContextGetMessage(con, buf, &receiveLen, &subID, sizeof(buf), true);
+    if (!id && receiveLen) {
+        LOGV("message receive buffer too small.  %i", receiveLen);
+    }
+    return _env->NewStringUTF(buf);
+}
+
+static void
+nContextGetUserMessage(JNIEnv *_env, jobject _this, RsContext con, jintArray data)
 {
     jint len = _env->GetArrayLength(data);
     LOG_API("nContextGetMessage, con(%p), len(%i)", con, len);
     jint *ptr = _env->GetIntArrayElements(data, NULL);
     size_t receiveLen;
-    int id = rsContextGetMessage(con, ptr, &receiveLen, len * 4, wait);
+    uint32_t subID;
+    int id = rsContextGetMessage(con, ptr, &receiveLen, &subID, len * 4, true);
     if (!id && receiveLen) {
         LOGV("message receive buffer too small.  %i", receiveLen);
-        *ptr = (jint)receiveLen;
     }
     _env->ReleaseIntArrayElements(data, ptr, 0);
+}
+
+static jint
+nContextPeekMessage(JNIEnv *_env, jobject _this, RsContext con, jintArray auxData, jboolean wait)
+{
+    LOG_API("nContextPeekMessage, con(%p)", con);
+    jint *auxDataPtr = _env->GetIntArrayElements(auxData, NULL);
+    size_t receiveLen;
+    uint32_t subID;
+    int id = rsContextPeekMessage(con, &receiveLen, &subID, wait);
+    auxDataPtr[0] = (jint)subID;
+    auxDataPtr[1] = (jint)receiveLen;
+    _env->ReleaseIntArrayElements(auxData, auxDataPtr, 0);
     return id;
 }
 
@@ -1218,15 +1247,18 @@
 static const char *classPathName = "android/renderscript/RenderScript";
 
 static JNINativeMethod methods[] = {
-{"_nInit",                         "()V",                                  (void*)_nInit },
-{"nInitElements",                  "(IIII)V",                              (void*)nInitElements },
+{"_nInit",                         "()V",                                     (void*)_nInit },
+{"nInitElements",                  "(IIII)V",                                 (void*)nInitElements },
 
-{"nDeviceCreate",                  "()I",                                  (void*)nDeviceCreate },
-{"nDeviceDestroy",                 "(I)V",                                 (void*)nDeviceDestroy },
-{"nDeviceSetConfig",               "(III)V",                               (void*)nDeviceSetConfig },
-{"nContextGetMessage",             "(I[IZ)I",                               (void*)nContextGetMessage },
-{"nContextInitToClient",           "(I)V",                                  (void*)nContextInitToClient },
-{"nContextDeinitToClient",         "(I)V",                                  (void*)nContextDeinitToClient },
+{"nDeviceCreate",                  "()I",                                     (void*)nDeviceCreate },
+{"nDeviceDestroy",                 "(I)V",                                    (void*)nDeviceDestroy },
+{"nDeviceSetConfig",               "(III)V",                                  (void*)nDeviceSetConfig },
+{"nContextGetUserMessage",         "(I[I)V",                                  (void*)nContextGetUserMessage },
+{"nContextGetErrorMessage",        "(I)Ljava/lang/String;",                   (void*)nContextGetErrorMessage },
+{"nContextPeekMessage",            "(I[IZ)I",                                 (void*)nContextPeekMessage },
+
+{"nContextInitToClient",           "(I)V",                                    (void*)nContextInitToClient },
+{"nContextDeinitToClient",         "(I)V",                                    (void*)nContextDeinitToClient },
 
 
 // All methods below are thread protected in java.
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 8c2081d..20e289d 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -79,13 +79,24 @@
 RsContext rsContextCreateGL(RsDevice, uint32_t version, RsSurfaceConfig sc);
 void rsContextDestroy(RsContext);
 
-uint32_t rsContextGetMessage(RsContext, void *data, size_t *receiveLen, size_t bufferLen, bool wait);
+enum RsMessageToClientType {
+    RS_MESSAGE_TO_CLIENT_NONE = 0,
+    RS_MESSAGE_TO_CLIENT_EXCEPTION = 1,
+    RS_MESSAGE_TO_CLIENT_RESIZE = 2,
+    RS_MESSAGE_TO_CLIENT_ERROR = 3,
+    RS_MESSAGE_TO_CLIENT_USER = 4
+};
+
+RsMessageToClientType rsContextGetMessage(RsContext vrsc, void *data, size_t *receiveLen, uint32_t *subID, size_t bufferLen, bool wait);
+RsMessageToClientType rsContextPeekMessage(RsContext vrsc, size_t *receiveLen, uint32_t *subID, bool wait);
 void rsContextInitToClient(RsContext);
 void rsContextDeinitToClient(RsContext);
 
 #define RS_MAX_TEXTURE 2
 #define RS_MAX_ATTRIBS 16
 
+
+
 enum RsDataType {
     RS_TYPE_NONE,
     RS_TYPE_FLOAT_16,
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 678d327..9d766b7 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -846,53 +846,69 @@
     }
 }
 
-uint32_t Context::getMessageToClient(void *data, size_t *receiveLen, size_t bufferLen, bool wait)
+RsMessageToClientType Context::peekMessageToClient(size_t *receiveLen, uint32_t *subID, bool wait)
+{
+    *receiveLen = 0;
+    if (!wait && mIO.mToClient.isEmpty()) {
+        return RS_MESSAGE_TO_CLIENT_NONE;
+    }
+
+    uint32_t bytesData = 0;
+    uint32_t commandID = 0;
+    const uint32_t *d = (const uint32_t *)mIO.mToClient.get(&commandID, &bytesData);
+    *receiveLen = bytesData - sizeof(uint32_t);
+    if (bytesData) {
+        *subID = d[0];
+    }
+    return (RsMessageToClientType)commandID;
+}
+
+RsMessageToClientType Context::getMessageToClient(void *data, size_t *receiveLen, uint32_t *subID, size_t bufferLen, bool wait)
 {
     //LOGE("getMessageToClient %i %i", bufferLen, wait);
     *receiveLen = 0;
-    if (!wait) {
-        if (mIO.mToClient.isEmpty()) {
-            // No message to get and not going to wait for one.
-            return 0;
-        }
+    if (!wait && mIO.mToClient.isEmpty()) {
+        return RS_MESSAGE_TO_CLIENT_NONE;
     }
 
     //LOGE("getMessageToClient 2 con=%p", this);
     uint32_t bytesData = 0;
     uint32_t commandID = 0;
-    const void *d = mIO.mToClient.get(&commandID, &bytesData);
+    const uint32_t *d = (const uint32_t *)mIO.mToClient.get(&commandID, &bytesData);
     //LOGE("getMessageToClient 3    %i  %i", commandID, bytesData);
 
-    *receiveLen = bytesData;
+    *receiveLen = bytesData - sizeof(uint32_t);
+    *subID = d[0];
+
+    //LOGE("getMessageToClient  %i %i", commandID, *subID);
     if (bufferLen >= bytesData) {
-        memcpy(data, d, bytesData);
+        memcpy(data, d+1, *receiveLen);
         mIO.mToClient.next();
-        return commandID;
+        return (RsMessageToClientType)commandID;
     }
-    return 0;
+    return RS_MESSAGE_TO_CLIENT_RESIZE;
 }
 
-bool Context::sendMessageToClient(void *data, uint32_t cmdID, size_t len, bool waitForSpace)
+bool Context::sendMessageToClient(const void *data, RsMessageToClientType cmdID, uint32_t subID, size_t len, bool waitForSpace)
 {
-    //LOGE("sendMessageToClient %i %i %i", cmdID, len, waitForSpace);
+    //LOGE("sendMessageToClient %i %i %i %i", cmdID, subID, len, waitForSpace);
     if (cmdID == 0) {
         LOGE("Attempting to send invalid command 0 to client.");
         return false;
     }
     if (!waitForSpace) {
-        if (!mIO.mToClient.makeSpaceNonBlocking(len + 8)) {
+        if (!mIO.mToClient.makeSpaceNonBlocking(len + 12)) {
             // Not enough room, and not waiting.
             return false;
         }
     }
     //LOGE("sendMessageToClient 2");
+    uint32_t *p = (uint32_t *)mIO.mToClient.reserve(len + sizeof(subID));
+    p[0] = subID;
     if (len > 0) {
-        void *p = mIO.mToClient.reserve(len);
-        memcpy(p, data, len);
-        mIO.mToClient.commit(cmdID, len);
-    } else {
-        mIO.mToClient.commit(cmdID, 0);
+        memcpy(p+1, data, len);
     }
+    mIO.mToClient.commit(cmdID, len + sizeof(subID));
     //LOGE("sendMessageToClient 3");
     return true;
 }
@@ -923,6 +939,7 @@
 {
     mError = e;
     mErrorMsg = msg;
+    sendMessageToClient(msg, RS_MESSAGE_TO_CLIENT_ERROR, e, strlen(msg) + 1, true);
 }
 
 
@@ -1080,10 +1097,16 @@
     delete rsc;
 }
 
-uint32_t rsContextGetMessage(RsContext vrsc, void *data, size_t *receiveLen, size_t bufferLen, bool wait)
+RsMessageToClientType rsContextPeekMessage(RsContext vrsc, size_t *receiveLen, uint32_t *subID, bool wait)
 {
     Context * rsc = static_cast<Context *>(vrsc);
-    return rsc->getMessageToClient(data, receiveLen, bufferLen, wait);
+    return rsc->peekMessageToClient(receiveLen, subID, wait);
+}
+
+RsMessageToClientType rsContextGetMessage(RsContext vrsc, void *data, size_t *receiveLen, uint32_t *subID, size_t bufferLen, bool wait)
+{
+    Context * rsc = static_cast<Context *>(vrsc);
+    return rsc->getMessageToClient(data, receiveLen, subID, bufferLen, wait);
 }
 
 void rsContextInitToClient(RsContext vrsc)
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 2017ceb7..2b9e57a 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -128,8 +128,9 @@
     void assignName(ObjectBase *obj, const char *name, uint32_t len);
     void removeName(ObjectBase *obj);
 
-    uint32_t getMessageToClient(void *data, size_t *receiveLen, size_t bufferLen, bool wait);
-    bool sendMessageToClient(void *data, uint32_t cmdID, size_t len, bool waitForSpace);
+    RsMessageToClientType peekMessageToClient(size_t *receiveLen, uint32_t *subID, bool wait);
+    RsMessageToClientType getMessageToClient(void *data, size_t *receiveLen, uint32_t *subID, size_t bufferLen, bool wait);
+    bool sendMessageToClient(const void *data, RsMessageToClientType cmdID, uint32_t subID, size_t len, bool waitForSpace);
     uint32_t runScript(Script *s);
 
     void initToClient();
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index ecae306..bb8e6a7 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -347,28 +347,28 @@
 {
     GET_TLS();
     //LOGE("SC_toClient %i %i %i", cmdID, len);
-    return rsc->sendMessageToClient(data, cmdID, len, false);
+    return rsc->sendMessageToClient(data, RS_MESSAGE_TO_CLIENT_USER, cmdID, len, false);
 }
 
 static uint32_t SC_toClient(int cmdID)
 {
     GET_TLS();
     //LOGE("SC_toClient %i", cmdID);
-    return rsc->sendMessageToClient(NULL, cmdID, 0, false);
+    return rsc->sendMessageToClient(NULL, RS_MESSAGE_TO_CLIENT_USER, cmdID, 0, false);
 }
 
 static uint32_t SC_toClientBlocking2(int cmdID, void *data, int len)
 {
     GET_TLS();
     //LOGE("SC_toClientBlocking %i %i", cmdID, len);
-    return rsc->sendMessageToClient(data, cmdID, len, true);
+    return rsc->sendMessageToClient(data, RS_MESSAGE_TO_CLIENT_USER, cmdID, len, true);
 }
 
 static uint32_t SC_toClientBlocking(int cmdID)
 {
     GET_TLS();
     //LOGE("SC_toClientBlocking %i", cmdID);
-    return rsc->sendMessageToClient(NULL, cmdID, 0, true);
+    return rsc->sendMessageToClient(NULL, RS_MESSAGE_TO_CLIENT_USER, cmdID, 0, true);
 }
 
 int SC_divsi3(int a, int b)