// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using UnityEngine;
using UnityEngine.Scripting;
using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Collections.Generic;
namespace UnityEngine
{
public delegate void AndroidJavaRunnable();
public sealed class AndroidJavaException : Exception
{
private string mJavaStackTrace;
internal AndroidJavaException(string message, string javaStackTrace) : base(message)
{
mJavaStackTrace = javaStackTrace;
}
public override string StackTrace
{
get
{
return mJavaStackTrace + base.StackTrace;
}
}
}
internal class GlobalJavaObjectRef
{
public GlobalJavaObjectRef(IntPtr jobject)
{
m_jobject = (jobject == IntPtr.Zero) ? IntPtr.Zero : AndroidJNI.NewGlobalRef(jobject);
}
~GlobalJavaObjectRef()
{
Dispose();
}
public static implicit operator IntPtr(GlobalJavaObjectRef obj)
{
return obj.m_jobject;
}
private bool m_disposed = false;
public void Dispose()
{
if (m_disposed)
return;
m_disposed = true;
if (m_jobject != IntPtr.Zero)
{
AndroidJNISafe.DeleteGlobalRef(m_jobject);
}
}
protected IntPtr m_jobject;
}
internal class AndroidJavaRunnableProxy : AndroidJavaProxy
{
private AndroidJavaRunnable mRunnable;
public AndroidJavaRunnableProxy(AndroidJavaRunnable runnable) : base("java/lang/Runnable") { mRunnable = runnable; }
public void run() { mRunnable(); }
}
public class AndroidJavaProxy
{
public readonly AndroidJavaClass javaInterface;
public AndroidJavaProxy(string javaInterface) : this(new AndroidJavaClass(javaInterface)) {}
public AndroidJavaProxy(AndroidJavaClass javaInterface)
{
this.javaInterface = javaInterface;
}
~AndroidJavaProxy()
{
AndroidJNISafe.DeleteWeakGlobalRef(proxyObject);
}
public virtual AndroidJavaObject Invoke(string methodName, object[] args)
{
Exception error = null;
BindingFlags binderFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
Type[] argTypes = new Type[args.Length];
for (int i = 0; i < args.Length; ++i)
argTypes[i] = args[i] == null ? typeof(AndroidJavaObject) : args[i].GetType();
try
{
MethodInfo method = GetType().GetMethod(methodName, binderFlags, null, argTypes, null);
if (method != null)
return _AndroidJNIHelper.Box(method.Invoke(this, args));
}
catch (TargetInvocationException invocationError)
{
error = invocationError.InnerException;
}
catch (Exception invocationError)
{
error = invocationError;
}
// Log error
string[] argTypeNames = new string[args.Length];
for (int i = 0; i < argTypes.Length; ++i)
argTypeNames[i] = argTypes[i].ToString();
if (error != null)
throw new TargetInvocationException(GetType() + "." + methodName + "(" + string.Join(",", argTypeNames) + ")", error);
AndroidReflection.SetNativeExceptionOnProxy(GetRawProxy(),
new Exception("No such proxy method: " + GetType() + "." + methodName + "(" + string.Join(",", argTypeNames) + ")"), true);
return null;
}
public virtual AndroidJavaObject Invoke(string methodName, AndroidJavaObject[] javaArgs)
{
object[] args = new object[javaArgs.Length];
for (int i = 0; i < javaArgs.Length; ++i)
{
args[i] = _AndroidJNIHelper.Unbox(javaArgs[i]);
if (!(args[i] is AndroidJavaObject))
{
// If we're not passing a AndroidJavaObject/Class to the proxy, we can safely dispose
// Otherwise the GC would do it eventally, but it might be too slow and hit the global ref limit
if (javaArgs[i] != null)
javaArgs[i].Dispose();
}
}
return Invoke(methodName, args);
}
// implementing equals, hashCode and toString which should be implemented by all java objects
// these methods must be in camel case, because that's how they are defined in java.
public virtual bool equals(AndroidJavaObject obj)
{
IntPtr anotherObject = (obj == null) ? System.IntPtr.Zero : obj.GetRawObject();
return AndroidJNI.IsSameObject(proxyObject, anotherObject);
}
public virtual int hashCode()
{
jvalue[] jniArgs = new jvalue[1];
jniArgs[0].l = GetRawProxy();
return (int)AndroidJNISafe.CallStaticIntMethod(s_JavaLangSystemClass, s_HashCodeMethodID, jniArgs);
}
public virtual string toString()
{
return this + " ";
}
internal IntPtr proxyObject = IntPtr.Zero;
internal AndroidJavaObject GetProxyObject()
{
return AndroidJavaObject.AndroidJavaObjectDeleteLocalRef(GetRawProxy());
}
internal IntPtr GetRawProxy()
{
IntPtr ret = IntPtr.Zero;
if (proxyObject != IntPtr.Zero)
{
ret = AndroidJNI.NewLocalRef(proxyObject);
if (ret == IntPtr.Zero)
{
AndroidJNI.DeleteWeakGlobalRef(proxyObject);
proxyObject = IntPtr.Zero;
}
}
if (ret == IntPtr.Zero)
{
ret = AndroidJNIHelper.CreateJavaProxy(this);
proxyObject = AndroidJNI.NewWeakGlobalRef(ret);
}
return ret;
}
private static readonly GlobalJavaObjectRef s_JavaLangSystemClass = new GlobalJavaObjectRef(AndroidJNISafe.FindClass("java/lang/System"));
private static readonly IntPtr s_HashCodeMethodID = AndroidJNIHelper.GetMethodID(s_JavaLangSystemClass, "identityHashCode", "(Ljava/lang/Object;)I", true);
}
public class AndroidJavaObject : IDisposable
{
// Construct an AndroidJavaObject based on the name of the class.
public AndroidJavaObject(string className, string[] args) : this()
{
_AndroidJavaObject(className, (object)args);
}
public AndroidJavaObject(string className, AndroidJavaObject[] args) : this()
{
_AndroidJavaObject(className, (object)args);
}
public AndroidJavaObject(string className, AndroidJavaClass[] args) : this()
{
_AndroidJavaObject(className, (object)args);
}
public AndroidJavaObject(string className, AndroidJavaProxy[] args) : this()
{
_AndroidJavaObject(className, (object)args);
}
public AndroidJavaObject(string className, AndroidJavaRunnable[] args) : this()
{
_AndroidJavaObject(className, (object)args);
}
public AndroidJavaObject(string className, params object[] args) : this()
{
_AndroidJavaObject(className, args);
}
//===================================================================
// IDisposable callback
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
//===================================================================
// Calls a Java method on an object (non-static).
public void Call(string methodName, T[] args)
{
_Call(methodName, (object)args);
}
public void Call(string methodName, params object[] args)
{
_Call(methodName, args);
}
//===================================================================
// Call a static Java method on a class.
public void CallStatic(string methodName, T[] args)
{
_CallStatic(methodName, (object)args);
}
public void CallStatic(string methodName, params object[] args)
{
_CallStatic(methodName, args);
}
//===================================================================
// Get the value of a field in an object (non-static).
public FieldType Get(string fieldName)
{
return _Get(fieldName);
}
// Set the value of a field in an object (non-static).
public void Set(string fieldName, FieldType val)
{
_Set(fieldName, val);
}
//===================================================================
// Get the value of a static field in an object type.
public FieldType GetStatic(string fieldName)
{
return _GetStatic(fieldName);
}
// Set the value of a static field in an object type.
public void SetStatic(string fieldName, FieldType val)
{
_SetStatic(fieldName, val);
}
//===================================================================
// Retrieve the raw jobject pointer to the Java object.
public IntPtr GetRawObject()
{
return _GetRawObject();
}
// Retrieve the raw jclass pointer to the Java class;
public IntPtr GetRawClass()
{
return _GetRawClass();
}
//===================================================================
// Call a Java method on an object.
public ReturnType Call(string methodName, T[] args)
{
return _Call(methodName, (object)args);
}
public ReturnType Call(string methodName, params object[] args)
{
return _Call(methodName, args);
}
// Call a static Java method on a class.
public ReturnType CallStatic(string methodName, T[] args)
{
return _CallStatic(methodName, (object)args);
}
public ReturnType CallStatic(string methodName, params object[] args)
{
return _CallStatic(methodName, args);
}
//===================================================================
private static bool enableDebugPrints = false;
protected void DebugPrint(string msg)
{
if (!enableDebugPrints)
return;
Debug.Log(msg);
}
protected void DebugPrint(string call, string methodName, string signature, object[] args)
{
if (!enableDebugPrints)
return;
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (object obj in args)
{
sb.Append(", ");
sb.Append(obj == null ? "" : obj.GetType().ToString());
}
Debug.Log(call + "(\"" + methodName + "\"" + sb + ") = " + signature);
}
//===================================================================
private void _AndroidJavaObject(string className, params object[] args)
{
DebugPrint("Creating AndroidJavaObject from " + className);
if (args == null) args = new object[] { null };
var clazz = AndroidJNISafe.FindClass(className.Replace('.', '/'));
m_jclass = new GlobalJavaObjectRef(clazz);
jvalue[] jniArgs = AndroidJNIHelper.CreateJNIArgArray(args);
try
{
IntPtr constructorID = AndroidJNIHelper.GetConstructorID(m_jclass, args);
IntPtr jobject = AndroidJNISafe.NewObject(m_jclass, constructorID, jniArgs);
m_jobject = new GlobalJavaObjectRef(jobject);
AndroidJNISafe.DeleteLocalRef(jobject);
}
finally
{
AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
}
}
internal AndroidJavaObject(IntPtr jobject) : this() // should be protected and friends with AndroidJNIHelper..
{
if (jobject == IntPtr.Zero)
{
throw new Exception("JNI: Init'd AndroidJavaObject with null ptr!");
}
IntPtr jclass = AndroidJNISafe.GetObjectClass(jobject);
m_jobject = new GlobalJavaObjectRef(jobject);
m_jclass = new GlobalJavaObjectRef(jclass);
AndroidJNISafe.DeleteLocalRef(jclass);
}
internal AndroidJavaObject()
{
}
~AndroidJavaObject()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (m_jobject != null)
{
m_jobject.Dispose();
m_jobject = null;
}
if (m_jclass != null)
{
m_jclass.Dispose();
m_jclass = null;
}
}
//===================================================================
protected void _Call(string methodName, params object[] args)
{
if (args == null) args = new object[] { null };
IntPtr methodID = AndroidJNIHelper.GetMethodID(m_jclass, methodName, args, false);
jvalue[] jniArgs = AndroidJNIHelper.CreateJNIArgArray(args);
try
{
AndroidJNISafe.CallVoidMethod(m_jobject, methodID, jniArgs);
}
finally
{
AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
}
}
protected ReturnType _Call(string methodName, params object[] args)
{
if (args == null) args = new object[] { null };
IntPtr methodID = AndroidJNIHelper.GetMethodID(m_jclass, methodName, args, false);
jvalue[] jniArgs = AndroidJNIHelper.CreateJNIArgArray(args);
try
{
if (AndroidReflection.IsPrimitive(typeof(ReturnType)))
{
if (typeof(ReturnType) == typeof(Int32))
return (ReturnType)(object)AndroidJNISafe.CallIntMethod(m_jobject, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Boolean))
return (ReturnType)(object)AndroidJNISafe.CallBooleanMethod(m_jobject, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Byte))
{
Debug.LogWarning("Return type for Java method call is obsolete, use return type instead");
return (ReturnType)(object)(Byte)AndroidJNISafe.CallSByteMethod(m_jobject, methodID, jniArgs);
}
else if (typeof(ReturnType) == typeof(SByte))
return (ReturnType)(object)AndroidJNISafe.CallSByteMethod(m_jobject, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Int16))
return (ReturnType)(object)AndroidJNISafe.CallShortMethod(m_jobject, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Int64))
return (ReturnType)(object)AndroidJNISafe.CallLongMethod(m_jobject, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Single))
return (ReturnType)(object)AndroidJNISafe.CallFloatMethod(m_jobject, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Double))
return (ReturnType)(object)AndroidJNISafe.CallDoubleMethod(m_jobject, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Char))
return (ReturnType)(object)AndroidJNISafe.CallCharMethod(m_jobject, methodID, jniArgs);
}
else if (typeof(ReturnType) == typeof(String))
return (ReturnType)(object)AndroidJNISafe.CallStringMethod(m_jobject, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(AndroidJavaClass))
{
IntPtr jclass = AndroidJNISafe.CallObjectMethod(m_jobject, methodID, jniArgs);
return (jclass == IntPtr.Zero) ? default(ReturnType) : (ReturnType)(object)AndroidJavaClassDeleteLocalRef(jclass);
}
else if (typeof(ReturnType) == typeof(AndroidJavaObject))
{
IntPtr jobject = AndroidJNISafe.CallObjectMethod(m_jobject, methodID, jniArgs);
return (jobject == IntPtr.Zero) ? default(ReturnType) : (ReturnType)(object)AndroidJavaObjectDeleteLocalRef(jobject);
}
else if (AndroidReflection.IsAssignableFrom(typeof(System.Array), typeof(ReturnType)))
{
IntPtr jobject = AndroidJNISafe.CallObjectMethod(m_jobject, methodID, jniArgs);
return (jobject == IntPtr.Zero) ? default(ReturnType) : (ReturnType)(object)AndroidJNIHelper.ConvertFromJNIArray(jobject);
}
else
{
throw new Exception("JNI: Unknown return type '" + typeof(ReturnType) + "'");
}
return default(ReturnType);
}
finally
{
AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
}
}
//===================================================================
protected FieldType _Get(string fieldName)
{
IntPtr fieldID = AndroidJNIHelper.GetFieldID(m_jclass, fieldName, false);
if (AndroidReflection.IsPrimitive(typeof(FieldType)))
{
if (typeof(FieldType) == typeof(Int32))
return (FieldType)(object)AndroidJNISafe.GetIntField(m_jobject, fieldID);
else if (typeof(FieldType) == typeof(Boolean))
return (FieldType)(object)AndroidJNISafe.GetBooleanField(m_jobject, fieldID);
else if (typeof(FieldType) == typeof(Byte))
{
Debug.LogWarning("Field type for Java get field call is obsolete, use field type instead");
return (FieldType)(object)(Byte)AndroidJNISafe.GetSByteField(m_jobject, fieldID);
}
else if (typeof(FieldType) == typeof(SByte))
return (FieldType)(object)AndroidJNISafe.GetSByteField(m_jobject, fieldID);
else if (typeof(FieldType) == typeof(Int16))
return (FieldType)(object)AndroidJNISafe.GetShortField(m_jobject, fieldID);
else if (typeof(FieldType) == typeof(Int64))
return (FieldType)(object)AndroidJNISafe.GetLongField(m_jobject, fieldID);
else if (typeof(FieldType) == typeof(Single))
return (FieldType)(object)AndroidJNISafe.GetFloatField(m_jobject, fieldID);
else if (typeof(FieldType) == typeof(Double))
return (FieldType)(object)AndroidJNISafe.GetDoubleField(m_jobject, fieldID);
else if (typeof(FieldType) == typeof(Char))
return (FieldType)(object)AndroidJNISafe.GetCharField(m_jobject, fieldID);
}
else if (typeof(FieldType) == typeof(String))
return (FieldType)(object)AndroidJNISafe.GetStringField(m_jobject, fieldID);
else if (typeof(FieldType) == typeof(AndroidJavaClass))
{
IntPtr jclass = AndroidJNISafe.GetObjectField(m_jobject, fieldID);
return (jclass == IntPtr.Zero) ? default(FieldType) : (FieldType)(object)AndroidJavaClassDeleteLocalRef(jclass);
}
else if (typeof(FieldType) == typeof(AndroidJavaObject))
{
IntPtr jobject = AndroidJNISafe.GetObjectField(m_jobject, fieldID);
return (jobject == IntPtr.Zero) ? default(FieldType) : (FieldType)(object)AndroidJavaObjectDeleteLocalRef(jobject);
}
else if (AndroidReflection.IsAssignableFrom(typeof(System.Array), typeof(FieldType)))
{
IntPtr jobject = AndroidJNISafe.GetObjectField(m_jobject, fieldID);
return (jobject == IntPtr.Zero) ? default(FieldType) : (FieldType)(object)AndroidJNIHelper.ConvertFromJNIArray(jobject);
}
else
{
throw new Exception("JNI: Unknown field type '" + typeof(FieldType) + "'");
}
return default(FieldType);
}
protected void _Set(string fieldName, FieldType val)
{
IntPtr fieldID = AndroidJNIHelper.GetFieldID(m_jclass, fieldName, false);
if (AndroidReflection.IsPrimitive(typeof(FieldType)))
{
if (typeof(FieldType) == typeof(Int32))
AndroidJNISafe.SetIntField(m_jobject, fieldID, (Int32)(object)val);
else if (typeof(FieldType) == typeof(Boolean))
AndroidJNISafe.SetBooleanField(m_jobject, fieldID, (Boolean)(object)val);
else if (typeof(FieldType) == typeof(Byte))
{
Debug.LogWarning("Field type for Java set field call is obsolete, use field type instead");
AndroidJNISafe.SetSByteField(m_jobject, fieldID, (SByte)(Byte)(object)val);
}
else if (typeof(FieldType) == typeof(SByte))
AndroidJNISafe.SetSByteField(m_jobject, fieldID, (SByte)(object)val);
else if (typeof(FieldType) == typeof(Int16))
AndroidJNISafe.SetShortField(m_jobject, fieldID, (Int16)(object)val);
else if (typeof(FieldType) == typeof(Int64))
AndroidJNISafe.SetLongField(m_jobject, fieldID, (Int64)(object)val);
else if (typeof(FieldType) == typeof(Single))
AndroidJNISafe.SetFloatField(m_jobject, fieldID, (Single)(object)val);
else if (typeof(FieldType) == typeof(Double))
AndroidJNISafe.SetDoubleField(m_jobject, fieldID, (Double)(object)val);
else if (typeof(FieldType) == typeof(Char))
AndroidJNISafe.SetCharField(m_jobject, fieldID, (Char)(object)val);
}
else if (typeof(FieldType) == typeof(String))
AndroidJNISafe.SetStringField(m_jobject, fieldID, (String)(object)val);
else if (typeof(FieldType) == typeof(AndroidJavaClass))
{
AndroidJNISafe.SetObjectField(m_jobject, fieldID, val == null ? IntPtr.Zero : ((AndroidJavaClass)(object)val).m_jclass);
}
else if (typeof(FieldType) == typeof(AndroidJavaObject))
{
AndroidJNISafe.SetObjectField(m_jobject, fieldID, val == null ? IntPtr.Zero : ((AndroidJavaObject)(object)val).m_jobject);
}
else if (AndroidReflection.IsAssignableFrom(typeof(System.Array), typeof(FieldType)))
{
IntPtr jobject = AndroidJNIHelper.ConvertToJNIArray((Array)(object)val);
AndroidJNISafe.SetObjectField(m_jclass, fieldID, jobject);
}
else
{
throw new Exception("JNI: Unknown field type '" + typeof(FieldType) + "'");
}
}
//===================================================================
protected void _CallStatic(string methodName, params object[] args)
{
if (args == null) args = new object[] { null };
IntPtr methodID = AndroidJNIHelper.GetMethodID(m_jclass, methodName, args, true);
jvalue[] jniArgs = AndroidJNIHelper.CreateJNIArgArray(args);
try
{
AndroidJNISafe.CallStaticVoidMethod(m_jclass, methodID, jniArgs);
}
finally
{
AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
}
}
protected ReturnType _CallStatic(string methodName, params object[] args)
{
if (args == null) args = new object[] { null };
IntPtr methodID = AndroidJNIHelper.GetMethodID(m_jclass, methodName, args, true);
jvalue[] jniArgs = AndroidJNIHelper.CreateJNIArgArray(args);
try
{
if (AndroidReflection.IsPrimitive(typeof(ReturnType)))
{
if (typeof(ReturnType) == typeof(Int32))
return (ReturnType)(object)AndroidJNISafe.CallStaticIntMethod(m_jclass, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Boolean))
return (ReturnType)(object)AndroidJNISafe.CallStaticBooleanMethod(m_jclass, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Byte))
{
Debug.LogWarning("Return type for Java method call is obsolete, use return type instead");
return (ReturnType)(object)(Byte)AndroidJNISafe.CallStaticSByteMethod(m_jclass, methodID, jniArgs);
}
else if (typeof(ReturnType) == typeof(SByte))
return (ReturnType)(object)AndroidJNISafe.CallStaticSByteMethod(m_jclass, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Int16))
return (ReturnType)(object)AndroidJNISafe.CallStaticShortMethod(m_jclass, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Int64))
return (ReturnType)(object)AndroidJNISafe.CallStaticLongMethod(m_jclass, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Single))
return (ReturnType)(object)AndroidJNISafe.CallStaticFloatMethod(m_jclass, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Double))
return (ReturnType)(object)AndroidJNISafe.CallStaticDoubleMethod(m_jclass, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(Char))
return (ReturnType)(object)AndroidJNISafe.CallStaticCharMethod(m_jclass, methodID, jniArgs);
}
else if (typeof(ReturnType) == typeof(String))
return (ReturnType)(object)AndroidJNISafe.CallStaticStringMethod(m_jclass, methodID, jniArgs);
else if (typeof(ReturnType) == typeof(AndroidJavaClass))
{
IntPtr jclass = AndroidJNISafe.CallStaticObjectMethod(m_jclass, methodID, jniArgs);
return (jclass == IntPtr.Zero) ? default(ReturnType) : (ReturnType)(object)AndroidJavaClassDeleteLocalRef(jclass);
}
else if (typeof(ReturnType) == typeof(AndroidJavaObject))
{
IntPtr jobject = AndroidJNISafe.CallStaticObjectMethod(m_jclass, methodID, jniArgs);
return (jobject == IntPtr.Zero) ? default(ReturnType) : (ReturnType)(object)AndroidJavaObjectDeleteLocalRef(jobject);
}
else if (AndroidReflection.IsAssignableFrom(typeof(System.Array), typeof(ReturnType)))
{
IntPtr jobject = AndroidJNISafe.CallStaticObjectMethod(m_jclass, methodID, jniArgs);
return (jobject == IntPtr.Zero) ? default(ReturnType) : (ReturnType)(object)AndroidJNIHelper.ConvertFromJNIArray(jobject);
}
else
{
throw new Exception("JNI: Unknown return type '" + typeof(ReturnType) + "'");
}
return default(ReturnType);
}
finally
{
AndroidJNIHelper.DeleteJNIArgArray(args, jniArgs);
}
}
//===================================================================
protected FieldType _GetStatic(string fieldName)
{
IntPtr fieldID = AndroidJNIHelper.GetFieldID(m_jclass, fieldName, true);
if (AndroidReflection.IsPrimitive(typeof(FieldType)))
{
if (typeof(FieldType) == typeof(Int32))
return (FieldType)(object)AndroidJNISafe.GetStaticIntField(m_jclass, fieldID);
else if (typeof(FieldType) == typeof(Boolean))
return (FieldType)(object)AndroidJNISafe.GetStaticBooleanField(m_jclass, fieldID);
else if (typeof(FieldType) == typeof(Byte))
{
Debug.LogWarning("Field type for Java get field call is obsolete, use field type instead");
return (FieldType)(object)(Byte)AndroidJNISafe.GetStaticSByteField(m_jclass, fieldID);
}
else if (typeof(FieldType) == typeof(SByte))
return (FieldType)(object)AndroidJNISafe.GetStaticSByteField(m_jclass, fieldID);
else if (typeof(FieldType) == typeof(Int16))
return (FieldType)(object)AndroidJNISafe.GetStaticShortField(m_jclass, fieldID);
else if (typeof(FieldType) == typeof(Int64))
return (FieldType)(object)AndroidJNISafe.GetStaticLongField(m_jclass, fieldID);
else if (typeof(FieldType) == typeof(Single))
return (FieldType)(object)AndroidJNISafe.GetStaticFloatField(m_jclass, fieldID);
else if (typeof(FieldType) == typeof(Double))
return (FieldType)(object)AndroidJNISafe.GetStaticDoubleField(m_jclass, fieldID);
else if (typeof(FieldType) == typeof(Char))
return (FieldType)(object)AndroidJNISafe.GetStaticCharField(m_jclass, fieldID);
}
else if (typeof(FieldType) == typeof(String))
return (FieldType)(object)AndroidJNISafe.GetStaticStringField(m_jclass, fieldID);
else if (typeof(FieldType) == typeof(AndroidJavaClass))
{
IntPtr jclass = AndroidJNISafe.GetStaticObjectField(m_jclass, fieldID);
return (jclass == IntPtr.Zero) ? default(FieldType) : (FieldType)(object)AndroidJavaClassDeleteLocalRef(jclass);
}
else if (typeof(FieldType) == typeof(AndroidJavaObject))
{
IntPtr jobject = AndroidJNISafe.GetStaticObjectField(m_jclass, fieldID);
return (jobject == IntPtr.Zero) ? default(FieldType) : (FieldType)(object)AndroidJavaObjectDeleteLocalRef(jobject);
}
else if (AndroidReflection.IsAssignableFrom(typeof(System.Array), typeof(FieldType)))
{
IntPtr jobject = AndroidJNISafe.GetStaticObjectField(m_jclass, fieldID);
return (jobject == IntPtr.Zero) ? default(FieldType) : (FieldType)(object)AndroidJNIHelper.ConvertFromJNIArray(jobject);
}
else
{
throw new Exception("JNI: Unknown field type '" + typeof(FieldType) + "'");
}
return default(FieldType);
}
protected void _SetStatic(string fieldName, FieldType val)
{
IntPtr fieldID = AndroidJNIHelper.GetFieldID(m_jclass, fieldName, true);
if (AndroidReflection.IsPrimitive(typeof(FieldType)))
{
if (typeof(FieldType) == typeof(Int32))
AndroidJNISafe.SetStaticIntField(m_jclass, fieldID, (Int32)(object)val);
else if (typeof(FieldType) == typeof(Boolean))
AndroidJNISafe.SetStaticBooleanField(m_jclass, fieldID, (Boolean)(object)val);
else if (typeof(FieldType) == typeof(Byte))
{
Debug.LogWarning("Field type for Java set field call is obsolete, use field type instead");
AndroidJNISafe.SetStaticSByteField(m_jclass, fieldID, (SByte)(Byte)(object)val);
}
else if (typeof(FieldType) == typeof(SByte))
AndroidJNISafe.SetStaticSByteField(m_jclass, fieldID, (SByte)(object)val);
else if (typeof(FieldType) == typeof(Int16))
AndroidJNISafe.SetStaticShortField(m_jclass, fieldID, (Int16)(object)val);
else if (typeof(FieldType) == typeof(Int64))
AndroidJNISafe.SetStaticLongField(m_jclass, fieldID, (Int64)(object)val);
else if (typeof(FieldType) == typeof(Single))
AndroidJNISafe.SetStaticFloatField(m_jclass, fieldID, (Single)(object)val);
else if (typeof(FieldType) == typeof(Double))
AndroidJNISafe.SetStaticDoubleField(m_jclass, fieldID, (Double)(object)val);
else if (typeof(FieldType) == typeof(Char))
AndroidJNISafe.SetStaticCharField(m_jclass, fieldID, (Char)(object)val);
}
else if (typeof(FieldType) == typeof(String))
AndroidJNISafe.SetStaticStringField(m_jclass, fieldID, (String)(object)val);
else if (typeof(FieldType) == typeof(AndroidJavaClass))
{
AndroidJNISafe.SetStaticObjectField(m_jclass, fieldID, val == null ? IntPtr.Zero : ((AndroidJavaClass)(object)val).m_jclass);
}
else if (typeof(FieldType) == typeof(AndroidJavaObject))
{
AndroidJNISafe.SetStaticObjectField(m_jclass, fieldID, val == null ? IntPtr.Zero : ((AndroidJavaObject)(object)val).m_jobject);
}
else if (AndroidReflection.IsAssignableFrom(typeof(System.Array), typeof(FieldType)))
{
IntPtr jobject = AndroidJNIHelper.ConvertToJNIArray((Array)(object)val);
AndroidJNISafe.SetStaticObjectField(m_jclass, fieldID, jobject);
}
else
{
throw new Exception("JNI: Unknown field type '" + typeof(FieldType) + "'");
}
}
internal static AndroidJavaObject AndroidJavaObjectDeleteLocalRef(IntPtr jobject)
{
try { return new AndroidJavaObject(jobject); } finally { AndroidJNISafe.DeleteLocalRef(jobject); }
}
internal static AndroidJavaClass AndroidJavaClassDeleteLocalRef(IntPtr jclass)
{
try { return new AndroidJavaClass(jclass); } finally { AndroidJNISafe.DeleteLocalRef(jclass); }
}
//===================================================================
protected IntPtr _GetRawObject() { return m_jobject; }
protected IntPtr _GetRawClass() { return m_jclass; }
internal GlobalJavaObjectRef m_jobject;
internal GlobalJavaObjectRef m_jclass; // use this for static lookups; reset in subclases
}
public class AndroidJavaClass : AndroidJavaObject
{
// Construct an AndroidJavaClass from the class name
public AndroidJavaClass(string className) : base()
{
_AndroidJavaClass(className);
}
private void _AndroidJavaClass(string className)
{
DebugPrint("Creating AndroidJavaClass from " + className);
var clazz = AndroidJNISafe.FindClass(className.Replace('.', '/'));
m_jclass = new GlobalJavaObjectRef(clazz /*.GetRawObject()*/);
m_jobject = new GlobalJavaObjectRef(IntPtr.Zero);
}
internal AndroidJavaClass(IntPtr jclass) // should be protected and friends with AndroidJNIHelper..
{
if (jclass == IntPtr.Zero)
{
throw new Exception("JNI: Init'd AndroidJavaClass with null ptr!");
}
m_jclass = new GlobalJavaObjectRef(jclass);
m_jobject = new GlobalJavaObjectRef(IntPtr.Zero);
}
}
internal class AndroidReflection
{
public static bool IsPrimitive(System.Type t)
{
return t.IsPrimitive;
}
public static bool IsAssignableFrom(System.Type t, System.Type from)
{
return t.IsAssignableFrom(from);
}
private static IntPtr GetStaticMethodID(string clazz, string methodName, string signature)
{
IntPtr jclass = AndroidJNISafe.FindClass(clazz);
try
{
return AndroidJNISafe.GetStaticMethodID(jclass, methodName, signature);
}
finally
{
AndroidJNISafe.DeleteLocalRef(jclass);
}
}
private static IntPtr GetMethodID(string clazz, string methodName, string signature)
{
IntPtr jclass = AndroidJNISafe.FindClass(clazz);
try
{
return AndroidJNISafe.GetMethodID(jclass, methodName, signature);
}
finally
{
AndroidJNISafe.DeleteLocalRef(jclass);
}
}
private const string RELECTION_HELPER_CLASS_NAME = "com/unity3d/player/ReflectionHelper";
private static readonly GlobalJavaObjectRef s_ReflectionHelperClass = new GlobalJavaObjectRef(AndroidJNISafe.FindClass(RELECTION_HELPER_CLASS_NAME));
private static readonly IntPtr s_ReflectionHelperGetConstructorID = GetStaticMethodID(RELECTION_HELPER_CLASS_NAME, "getConstructorID", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/reflect/Constructor;");
private static readonly IntPtr s_ReflectionHelperGetMethodID = GetStaticMethodID(RELECTION_HELPER_CLASS_NAME, "getMethodID", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/reflect/Method;");
private static readonly IntPtr s_ReflectionHelperGetFieldID = GetStaticMethodID(RELECTION_HELPER_CLASS_NAME, "getFieldID", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Z)Ljava/lang/reflect/Field;");
private static readonly IntPtr s_ReflectionHelperGetFieldSignature = GetStaticMethodID(RELECTION_HELPER_CLASS_NAME, "getFieldSignature", "(Ljava/lang/reflect/Field;)Ljava/lang/String;");
private static readonly IntPtr s_ReflectionHelperNewProxyInstance = GetStaticMethodID(RELECTION_HELPER_CLASS_NAME, "newProxyInstance", "(JLjava/lang/Class;)Ljava/lang/Object;");
private static readonly IntPtr s_ReflectionHelperSetNativeExceptionOnProxy = GetStaticMethodID(RELECTION_HELPER_CLASS_NAME, "setNativeExceptionOnProxy", "(Ljava/lang/Object;JZ)V");
private static readonly IntPtr s_FieldGetDeclaringClass = GetMethodID("java/lang/reflect/Field", "getDeclaringClass", "()Ljava/lang/Class;");
public static IntPtr GetConstructorMember(IntPtr jclass, string signature)
{
jvalue[] jniArgs = new jvalue[2];
try
{
jniArgs[0].l = jclass;
jniArgs[1].l = AndroidJNISafe.NewString(signature);
return AndroidJNISafe.CallStaticObjectMethod(s_ReflectionHelperClass, s_ReflectionHelperGetConstructorID, jniArgs);
}
finally
{
AndroidJNISafe.DeleteLocalRef(jniArgs[1].l);
}
}
public static IntPtr GetMethodMember(IntPtr jclass, string methodName, string signature, bool isStatic)
{
jvalue[] jniArgs = new jvalue[4];
try
{
jniArgs[0].l = jclass;
jniArgs[1].l = AndroidJNISafe.NewString(methodName);
jniArgs[2].l = AndroidJNISafe.NewString(signature);
jniArgs[3].z = isStatic;
return AndroidJNISafe.CallStaticObjectMethod(s_ReflectionHelperClass, s_ReflectionHelperGetMethodID, jniArgs);
}
finally
{
AndroidJNISafe.DeleteLocalRef(jniArgs[1].l);
AndroidJNISafe.DeleteLocalRef(jniArgs[2].l);
}
}
public static IntPtr GetFieldMember(IntPtr jclass, string fieldName, string signature, bool isStatic)
{
jvalue[] jniArgs = new jvalue[4];
try
{
jniArgs[0].l = jclass;
jniArgs[1].l = AndroidJNISafe.NewString(fieldName);
jniArgs[2].l = AndroidJNISafe.NewString(signature);
jniArgs[3].z = isStatic;
return AndroidJNISafe.CallStaticObjectMethod(s_ReflectionHelperClass, s_ReflectionHelperGetFieldID, jniArgs);
}
finally
{
AndroidJNISafe.DeleteLocalRef(jniArgs[1].l);
AndroidJNISafe.DeleteLocalRef(jniArgs[2].l);
}
}
public static IntPtr GetFieldClass(IntPtr field)
{
return AndroidJNISafe.CallObjectMethod(field, s_FieldGetDeclaringClass, null);
}
public static string GetFieldSignature(IntPtr field)
{
jvalue[] jniArgs = new jvalue[1];
jniArgs[0].l = field;
return AndroidJNISafe.CallStaticStringMethod(s_ReflectionHelperClass, s_ReflectionHelperGetFieldSignature, jniArgs);
}
public static IntPtr NewProxyInstance(IntPtr delegateHandle, IntPtr interfaze)
{
jvalue[] jniArgs = new jvalue[2];
jniArgs[0].j = delegateHandle.ToInt64();
jniArgs[1].l = interfaze;
return AndroidJNISafe.CallStaticObjectMethod(s_ReflectionHelperClass, s_ReflectionHelperNewProxyInstance, jniArgs);
}
public static void SetNativeExceptionOnProxy(IntPtr proxy, Exception e, bool methodNotFound)
{
jvalue[] jniArgs = new jvalue[3];
jniArgs[0].l = proxy;
jniArgs[1].j = GCHandle.ToIntPtr(GCHandle.Alloc(e)).ToInt64();
jniArgs[2].z = methodNotFound;
AndroidJNISafe.CallStaticVoidMethod(s_ReflectionHelperClass, s_ReflectionHelperSetNativeExceptionOnProxy, jniArgs);
}
}
[UsedByNativeCode]
sealed class _AndroidJNIHelper
{
public static IntPtr CreateJavaProxy(IntPtr delegateHandle, AndroidJavaProxy proxy)
{
return AndroidReflection.NewProxyInstance(delegateHandle, proxy.javaInterface.GetRawClass());
}
public static IntPtr CreateJavaRunnable(AndroidJavaRunnable jrunnable)
{
return AndroidJNIHelper.CreateJavaProxy(new AndroidJavaRunnableProxy(jrunnable));
}
[RequiredByNativeCode]
public static IntPtr InvokeJavaProxyMethod(AndroidJavaProxy proxy, IntPtr jmethodName, IntPtr jargs)
{
try
{
int arrayLen = 0;
if (jargs != IntPtr.Zero)
{
arrayLen = AndroidJNISafe.GetArrayLength(jargs);
}
AndroidJavaObject[] args = new AndroidJavaObject[arrayLen];
for (int i = 0; i < arrayLen; ++i)
{
IntPtr objectRef = AndroidJNISafe.GetObjectArrayElement(jargs, i);
args[i] = objectRef != IntPtr.Zero ? new AndroidJavaObject(objectRef) : null;
}
using (AndroidJavaObject result = proxy.Invoke(AndroidJNI.GetStringChars(jmethodName), args))
{
if (result == null)
return IntPtr.Zero;
return AndroidJNI.NewLocalRef(result.GetRawObject());
}
}
catch (Exception e)
{
AndroidReflection.SetNativeExceptionOnProxy(proxy.GetRawProxy(), e, false);
return IntPtr.Zero;
}
}
public static jvalue[] CreateJNIArgArray(object[] args)
{
jvalue[] ret = new jvalue[args.GetLength(0)];
int i = 0;
foreach (object obj in args)
{
if (obj == null)
ret[i].l = System.IntPtr.Zero;
else if (AndroidReflection.IsPrimitive(obj.GetType()))
{
if (obj is System.Int32)
ret[i].i = (System.Int32)obj;
else if (obj is System.Boolean)
ret[i].z = (System.Boolean)obj;
else if (obj is System.Byte)
{
Debug.LogWarning("Passing Byte arguments to Java methods is obsolete, pass SByte parameters instead");
ret[i].b = (System.SByte)(System.Byte) obj;
}
else if (obj is System.SByte)
ret[i].b = (System.SByte)obj;
else if (obj is System.Int16)
ret[i].s = (System.Int16)obj;
else if (obj is System.Int64)
ret[i].j = (System.Int64)obj;
else if (obj is System.Single)
ret[i].f = (System.Single)obj;
else if (obj is System.Double)
ret[i].d = (System.Double)obj;
else if (obj is System.Char)
ret[i].c = (System.Char)obj;
}
else if (obj is System.String)
{
ret[i].l = AndroidJNISafe.NewString((System.String)obj);
}
else if (obj is AndroidJavaClass)
{
ret[i].l = ((AndroidJavaClass)obj).GetRawClass();
}
else if (obj is AndroidJavaObject)
{
ret[i].l = ((AndroidJavaObject)obj).GetRawObject();
}
else if (obj is System.Array)
{
ret[i].l = ConvertToJNIArray((System.Array)obj);
}
else if (obj is AndroidJavaProxy)
{
ret[i].l = ((AndroidJavaProxy)obj).GetRawProxy();
}
else if (obj is AndroidJavaRunnable)
{
ret[i].l = AndroidJNIHelper.CreateJavaRunnable((AndroidJavaRunnable)obj);
}
else
{
throw new Exception("JNI; Unknown argument type '" + obj.GetType() + "'");
}
++i;
}
return ret;
}
public static object UnboxArray(AndroidJavaObject obj)
{
if (obj == null)
return null;
AndroidJavaClass arrayUtil = new AndroidJavaClass("java/lang/reflect/Array");
AndroidJavaObject objClass = obj.Call("getClass");
AndroidJavaObject compClass = objClass.Call("getComponentType");
string className = compClass.Call("getName");
int arrayLength = arrayUtil.CallStatic("getLength", obj);
Array array;
if (compClass.Call("isPrimitive")) // need to setup primitive array
{
if ("int" == className)
array = new int[arrayLength];
else if ("boolean" == className)
array = new bool[arrayLength];
else if ("byte" == className)
array = new sbyte[arrayLength];
else if ("short" == className)
array = new short[arrayLength];
else if ("long" == className)
array = new long[arrayLength];
else if ("float" == className)
array = new float[arrayLength];
else if ("double" == className)
array = new double[arrayLength];
else if ("char" == className)
array = new char[arrayLength];
else
throw new Exception("JNI; Unknown argument type '" + className + "'");
}
else if ("java.lang.String" == className)
array = new string[arrayLength];
else if ("java.lang.Class" == className)
array = new AndroidJavaClass[arrayLength];
else
array = new AndroidJavaObject[arrayLength];
for (int i = 0; i < arrayLength; ++i)
array.SetValue(Unbox(arrayUtil.CallStatic("get", obj, i)), i);
arrayUtil.Dispose();
return array;
}
public static object Unbox(AndroidJavaObject obj)
{
if (obj == null)
return null;
using (AndroidJavaObject clazz = obj.Call("getClass"))
{
string className = clazz.Call("getName");
if ("java.lang.Integer" == className)
return obj.Call("intValue");
else if ("java.lang.Boolean" == className)
return obj.Call("booleanValue");
else if ("java.lang.Byte" == className)
return obj.Call("byteValue");
else if ("java.lang.Short" == className)
return obj.Call("shortValue");
else if ("java.lang.Long" == className)
return obj.Call("longValue");
else if ("java.lang.Float" == className)
return obj.Call("floatValue");
else if ("java.lang.Double" == className)
return obj.Call("doubleValue");
else if ("java.lang.Character" == className)
return obj.Call("charValue");
else if ("java.lang.String" == className)
return obj.Call("toString"); // um, can obvoiusly be performed in a better fasion
else if ("java.lang.Class" == className)
return new AndroidJavaClass(obj.GetRawObject());
else if (clazz.Call("isArray"))
return UnboxArray(obj);
else
return obj;
}
}
public static AndroidJavaObject Box(object obj)
{
if (obj == null)
return null;
else if (AndroidReflection.IsPrimitive(obj.GetType()))
{
if (obj is System.Int32)
return new AndroidJavaObject("java.lang.Integer", (System.Int32)obj);
else if (obj is System.Boolean)
return new AndroidJavaObject("java.lang.Boolean", (System.Boolean)obj);
else if (obj is System.Byte)
return new AndroidJavaObject("java.lang.Byte", (System.SByte)obj);
else if (obj is System.SByte)
return new AndroidJavaObject("java.lang.Byte", (System.SByte)obj);
else if (obj is System.Int16)
return new AndroidJavaObject("java.lang.Short", (System.Int16)obj);
else if (obj is System.Int64)
return new AndroidJavaObject("java.lang.Long", (System.Int64)obj);
else if (obj is System.Single)
return new AndroidJavaObject("java.lang.Float", (System.Single)obj);
else if (obj is System.Double)
return new AndroidJavaObject("java.lang.Double", (System.Double)obj);
else if (obj is System.Char)
return new AndroidJavaObject("java.lang.Character", (System.Char)obj);
else
throw new Exception("JNI; Unknown argument type '" + obj.GetType() + "'");
}
else if (obj is System.String)
{
return new AndroidJavaObject("java.lang.String", (System.String)obj);
}
else if (obj is AndroidJavaClass)
{
return new AndroidJavaObject(((AndroidJavaClass)obj).GetRawClass());
}
else if (obj is AndroidJavaObject)
{
return (AndroidJavaObject)obj;
}
else if (obj is System.Array)
{
return AndroidJavaObject.AndroidJavaObjectDeleteLocalRef(ConvertToJNIArray((System.Array)obj));
}
else if (obj is AndroidJavaProxy)
{
return ((AndroidJavaProxy)obj).GetProxyObject();
}
else if (obj is AndroidJavaRunnable)
{
return AndroidJavaObject.AndroidJavaObjectDeleteLocalRef(AndroidJNIHelper.CreateJavaRunnable((AndroidJavaRunnable)obj));
}
else
{
throw new Exception("JNI; Unknown argument type '" + obj.GetType() + "'");
}
}
public static void DeleteJNIArgArray(object[] args, jvalue[] jniArgs)
{
int i = 0;
foreach (object obj in args)
{
if (obj is System.String || obj is AndroidJavaRunnable || obj is System.Array)
AndroidJNISafe.DeleteLocalRef(jniArgs[i].l);
++i;
}
}
public static IntPtr ConvertToJNIArray(System.Array array)
{
Type type = array.GetType().GetElementType();
if (AndroidReflection.IsPrimitive(type))
{
if (type == typeof(Int32))
return AndroidJNISafe.ToIntArray((Int32[])array);
else if (type == typeof(Boolean))
return AndroidJNISafe.ToBooleanArray((Boolean[])array);
else if (type == typeof(Byte))
{
Debug.LogWarning("AndroidJNIHelper: converting Byte array is obsolete, use SByte array instead");
#pragma warning disable 0618
return AndroidJNISafe.ToByteArray((Byte[])array);
#pragma warning restore 0618
}
else if (type == typeof(SByte))
return AndroidJNISafe.ToSByteArray((SByte[])array);
else if (type == typeof(Int16))
return AndroidJNISafe.ToShortArray((Int16[])array);
else if (type == typeof(Int64))
return AndroidJNISafe.ToLongArray((Int64[])array);
else if (type == typeof(Single))
return AndroidJNISafe.ToFloatArray((Single[])array);
else if (type == typeof(Double))
return AndroidJNISafe.ToDoubleArray((Double[])array);
else if (type == typeof(Char))
return AndroidJNISafe.ToCharArray((Char[])array);
}
else if (type == typeof(String))
{
String[] strArray = (string[])array;
int arrayLen = array.GetLength(0);
IntPtr arrayType = AndroidJNISafe.FindClass("java/lang/String");
IntPtr res = AndroidJNI.NewObjectArray(arrayLen, arrayType, IntPtr.Zero);
for (int i = 0; i < arrayLen; ++i)
{
IntPtr jstring = AndroidJNISafe.NewString(strArray[i]);
AndroidJNI.SetObjectArrayElement(res, i, jstring);
AndroidJNISafe.DeleteLocalRef(jstring);
}
AndroidJNISafe.DeleteLocalRef(arrayType);
return res;
}
else if (type == typeof(AndroidJavaObject))
{
AndroidJavaObject[] objArray = (AndroidJavaObject[])array;
int arrayLen = array.GetLength(0);
IntPtr[] jniObjs = new IntPtr[arrayLen];
IntPtr fallBackType = AndroidJNISafe.FindClass("java/lang/Object");
IntPtr arrayType = IntPtr.Zero;
for (int i = 0; i < arrayLen; ++i)
{
if (objArray[i] != null)
{
jniObjs[i] = objArray[i].GetRawObject();
IntPtr objectType = objArray[i].GetRawClass();
if (arrayType != objectType)
{
if (arrayType == IntPtr.Zero)
{
arrayType = objectType;
}
else
{
arrayType = fallBackType; // java/lang/Object
}
}
}
else
{
jniObjs[i] = IntPtr.Zero;
}
}
// zero sized array will call this with IntPtr.Zero type translated into java/lang/Object
IntPtr res = AndroidJNISafe.ToObjectArray(jniObjs, arrayType);
AndroidJNISafe.DeleteLocalRef(fallBackType);
return res;
}
else
{
throw new Exception("JNI; Unknown array type '" + type + "'");
}
return IntPtr.Zero;
}
public static ArrayType ConvertFromJNIArray(IntPtr array)
{
Type type = typeof(ArrayType).GetElementType();
if (AndroidReflection.IsPrimitive(type))
{
if (type == typeof(Int32))
return (ArrayType)(object)AndroidJNISafe.FromIntArray(array);
else if (type == typeof(Boolean))
return (ArrayType)(object)AndroidJNISafe.FromBooleanArray(array);
else if (type == typeof(Byte))
{
Debug.LogWarning("AndroidJNIHelper: converting from Byte array is obsolete, use SByte array instead");
#pragma warning disable 0618
return (ArrayType)(object)AndroidJNISafe.FromByteArray(array);
#pragma warning restore 0618
}
else if (type == typeof(SByte))
return (ArrayType)(object)AndroidJNISafe.FromSByteArray(array);
else if (type == typeof(Int16))
return (ArrayType)(object)AndroidJNISafe.FromShortArray(array);
else if (type == typeof(Int64))
return (ArrayType)(object)AndroidJNISafe.FromLongArray(array);
else if (type == typeof(Single))
return (ArrayType)(object)AndroidJNISafe.FromFloatArray(array);
else if (type == typeof(Double))
return (ArrayType)(object)AndroidJNISafe.FromDoubleArray(array);
else if (type == typeof(Char))
return (ArrayType)(object)AndroidJNISafe.FromCharArray(array);
}
else if (type == typeof(String))
{
int arrayLen = AndroidJNISafe.GetArrayLength(array);
string[] strArray = new string[arrayLen];
for (int i = 0; i < arrayLen; ++i)
{
IntPtr jstring = AndroidJNI.GetObjectArrayElement(array, i);
strArray[i] = AndroidJNISafe.GetStringChars(jstring);
AndroidJNISafe.DeleteLocalRef(jstring);
}
return (ArrayType)(object)strArray;
}
else if (type == typeof(AndroidJavaObject))
{
int arrayLen = AndroidJNISafe.GetArrayLength(array);
AndroidJavaObject[] objArray = new AndroidJavaObject[arrayLen];
for (int i = 0; i < arrayLen; ++i)
{
IntPtr jobject = AndroidJNI.GetObjectArrayElement(array, i);
objArray[i] = new AndroidJavaObject(jobject);
AndroidJNISafe.DeleteLocalRef(jobject);
}
return (ArrayType)(object)objArray;
}
else
{
throw new Exception("JNI: Unknown generic array type '" + type + "'");
}
return default(ArrayType);
}
public static System.IntPtr GetConstructorID(System.IntPtr jclass, object[] args)
{
return AndroidJNIHelper.GetConstructorID(jclass, GetSignature(args));
}
public static System.IntPtr GetMethodID(System.IntPtr jclass, string methodName, object[] args, bool isStatic)
{
return AndroidJNIHelper.GetMethodID(jclass, methodName, GetSignature(args), isStatic);
}
public static System.IntPtr GetMethodID(System.IntPtr jclass, string methodName, object[] args, bool isStatic)
{
return AndroidJNIHelper.GetMethodID(jclass, methodName, GetSignature(args), isStatic);
}
public static System.IntPtr GetFieldID(System.IntPtr jclass, string fieldName, bool isStatic)
{
return AndroidJNIHelper.GetFieldID(jclass, fieldName, GetSignature(typeof(ReturnType)), isStatic);
}
public static IntPtr GetConstructorID(IntPtr jclass, string signature)
{
IntPtr constructor = IntPtr.Zero;
try
{
constructor = AndroidReflection.GetConstructorMember(jclass, signature);
return AndroidJNISafe.FromReflectedMethod(constructor);
}
catch (Exception e)
{
IntPtr memberID = AndroidJNISafe.GetMethodID(jclass, "", signature);
if (memberID != IntPtr.Zero)
return memberID;
throw e;
}
finally
{
AndroidJNISafe.DeleteLocalRef(constructor);
}
}
public static IntPtr GetMethodID(IntPtr jclass, string methodName, string signature, bool isStatic)
{
IntPtr method = IntPtr.Zero;
try
{
method = AndroidReflection.GetMethodMember(jclass, methodName, signature, isStatic);
return AndroidJNISafe.FromReflectedMethod(method);
}
catch (Exception e)
{
// Make sure this method does not throw to keep e intact
IntPtr memberID = GetMethodIDFallback(jclass, methodName, signature, isStatic);
if (memberID != IntPtr.Zero)
return memberID;
throw e;
}
finally
{
AndroidJNISafe.DeleteLocalRef(method);
}
}
private static IntPtr GetMethodIDFallback(IntPtr jclass, string methodName, string signature, bool isStatic)
{
try
{
return isStatic ?
AndroidJNISafe.GetStaticMethodID(jclass, methodName, signature) :
AndroidJNISafe.GetMethodID(jclass, methodName, signature);
}
catch (Exception)
{
// We don't want this exception to override the initial exception from AndroidReflection
}
return IntPtr.Zero;
}
public static IntPtr GetFieldID(IntPtr jclass, string fieldName, string signature, bool isStatic)
{
IntPtr memberID = IntPtr.Zero;
Exception reflectEx = null;
AndroidJNI.PushLocalFrame(10);
try
{
IntPtr field = AndroidReflection.GetFieldMember(jclass, fieldName, signature, isStatic);
if (!isStatic)
jclass = AndroidReflection.GetFieldClass(field);
signature = AndroidReflection.GetFieldSignature(field);
}
catch (Exception e)
{
reflectEx = e;
}
try
{
memberID = isStatic
? AndroidJNISafe.GetStaticFieldID(jclass, fieldName, signature)
: AndroidJNISafe.GetFieldID(jclass, fieldName, signature);
if (memberID == IntPtr.Zero)
{
if (reflectEx != null)
throw reflectEx;
throw new Exception(string.Format("Field {0} or type signature {1} not found", fieldName, signature));
}
return memberID;
}
finally
{
AndroidJNI.PopLocalFrame(IntPtr.Zero);
}
}
public static string GetSignature(object obj)
{
if (obj == null)
return "Ljava/lang/Object;";
System.Type type = (obj is System.Type) ? (System.Type)obj : obj.GetType();
if (AndroidReflection.IsPrimitive(type))
{
if (type.Equals(typeof(System.Int32)))
return "I";
else if (type.Equals(typeof(System.Boolean)))
return "Z";
else if (type.Equals(typeof(System.Byte)))
{
Debug.LogWarning("AndroidJNIHelper.GetSignature: using Byte parameters is obsolete, use SByte parameters instead");
return "B";
}
else if (type.Equals(typeof(System.SByte)))
return "B";
else if (type.Equals(typeof(System.Int16)))
return "S";
else if (type.Equals(typeof(System.Int64)))
return "J";
else if (type.Equals(typeof(System.Single)))
return "F";
else if (type.Equals(typeof(System.Double)))
return "D";
else if (type.Equals(typeof(System.Char)))
return "C";
}
else if (type.Equals(typeof(System.String)))
{
return "Ljava/lang/String;";
}
else if (obj is AndroidJavaProxy)
{
using (var javaClass = new AndroidJavaObject(((AndroidJavaProxy)obj).javaInterface.GetRawClass()))
{
return "L" + javaClass.Call("getName") + ";";
}
}
else if (type.Equals(typeof(AndroidJavaRunnable)))
{
return "Ljava/lang/Runnable;";
}
else if (type.Equals(typeof(AndroidJavaClass)))
{
return "Ljava/lang/Class;";
}
else if (type.Equals(typeof(AndroidJavaObject)))
{
if (obj == (object)type)
{
return "Ljava/lang/Object;";
}
AndroidJavaObject javaObject = (AndroidJavaObject)obj;
using (AndroidJavaObject javaClass = javaObject.Call("getClass"))
{
return "L" + javaClass.Call("getName") + ";";
}
}
else if (AndroidReflection.IsAssignableFrom(typeof(System.Array), type))
{
if (type.GetArrayRank() != 1)
{
throw new Exception("JNI: System.Array in n dimensions is not allowed");
}
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append('[');
sb.Append(GetSignature(type.GetElementType()));
return sb.ToString();
}
else
{
throw new Exception("JNI: Unknown signature for type '" + type + "' (obj = " + obj + ") " + ((object)type == obj ? "equal" : "instance"));
}
return "";
}
public static string GetSignature(object[] args)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append('(');
foreach (object obj in args)
{
sb.Append(GetSignature(obj));
}
sb.Append(")V");
return sb.ToString();
}
public static string GetSignature(object[] args)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append('(');
foreach (object obj in args)
{
sb.Append(GetSignature(obj));
}
sb.Append(')');
sb.Append(GetSignature(typeof(ReturnType)));
return sb.ToString();
}
}
}