From 9eaa051467c2fe6d7b77f82ba81e3b3a8fb3ce98 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Tue, 21 Dec 2021 22:37:36 -0500 Subject: [PATCH 01/16] [Xamarin.Android.Build.Tasks] MAM Member Remapping? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Context: https://github.com/xamarin/java.interop/issues/867 Context: https://github.com/xamarin/java.interop/pull/936 Does It Build? Does It Work? (Is It Sane?) For local "test" purposes, add a new `tools/remap-mam-json-to-xml` utility which parses the MAM JSON file into XML. $ dotnet run --project tools/remap-mam-json-to-xml -- \ $HOME/.nuget/packages/microsoft.intune.mam.remapper.tasks/0.1.4635.1/content/MonoAndroid10/remapping-config.json dotnet bin/Debug/net6.0/remap-mam-json-to-xml.dll $HOME/.nuget/packages/microsoft.intune.mam.remapper.tasks/0.1.4635.1/content/MonoAndroid10/remapping-config.json Add a new `@(_AndroidMamMappingFile)` item group which can be used to produce the mapping XML file. TODO: why is `$(_AndroidMamMappingXml)` e.g. `obj/Debug/assets/obj/Debug/assets/xa-mam-mapping.xml`? What's with the duplication? Update src/java-runtime to use `javac -h`, to emit JNI headers. (`javah` is not present in JDK11!) Update src/monodroid to depend on src/java-runtime, then use the headers generated by `javac -h` to ensure consistency. Rando aside: apparently `createNewContextWithData` was inconsistent?) --- .gitmodules | 4 +- external/Java.Interop | 2 +- .../HelloWorld/HelloWorld.DotNet.csproj | 15 ++ .../Android.Runtime/AndroidRuntime.cs | 85 ++++++- src/Mono.Android/Android.Runtime/JNIEnv.cs | 23 ++ .../Android.Runtime/MamXmlParser.cs | 135 +++++++++++ src/Mono.Android/Mono.Android.csproj | 2 + .../MamJsonToXml.cs | 33 +++ .../Utilities/MamJsonParser.cs | 215 ++++++++++++++++++ .../Xamarin.Android.Common.targets | 20 ++ src/java-runtime/java-runtime.targets | 6 +- .../java/mono/android/MonoPackageManager.java | 31 +++ .../java/mono/android/Runtime.java | 14 +- src/monodroid/jni/mono_android_Runtime.h | 7 +- src/monodroid/jni/monodroid-glue-designer.cc | 2 +- src/monodroid/jni/monodroid-glue-internal.hh | 11 +- src/monodroid/jni/monodroid-glue.cc | 30 ++- src/monodroid/monodroid.csproj | 1 + src/monodroid/monodroid.targets | 13 +- tools/remap-mam-json-to-xml/Program.cs | 47 ++++ .../remap-mam-json-to-xml.csproj | 29 +++ 21 files changed, 700 insertions(+), 25 deletions(-) create mode 100644 src/Mono.Android/Android.Runtime/MamXmlParser.cs create mode 100644 src/Xamarin.Android.Build.Tasks/MamJsonToXml.cs create mode 100644 src/Xamarin.Android.Build.Tasks/Utilities/MamJsonParser.cs create mode 100644 tools/remap-mam-json-to-xml/Program.cs create mode 100644 tools/remap-mam-json-to-xml/remap-mam-json-to-xml.csproj diff --git a/.gitmodules b/.gitmodules index 8b2fa1df307..60b2750388b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,8 +12,8 @@ branch = main [submodule "external/Java.Interop"] path = external/Java.Interop - url = https://github.com/xamarin/java.interop.git - branch = main + url = https://github.com/jonpryor/java.interop.git + branch = jonp-member-remapping [submodule "external/lz4"] path = external/lz4 url = https://github.com/lz4/lz4.git diff --git a/external/Java.Interop b/external/Java.Interop index 843f3c7817d..7699359dbcc 160000 --- a/external/Java.Interop +++ b/external/Java.Interop @@ -1 +1 @@ -Subproject commit 843f3c7817dc4bdae9ce69d04274f29fce574e09 +Subproject commit 7699359dbccbee7c7327ce9cb309ab7ca5adda83 diff --git a/samples/HelloWorld/HelloWorld/HelloWorld.DotNet.csproj b/samples/HelloWorld/HelloWorld/HelloWorld.DotNet.csproj index 996e0ddb2da..fd0c1a5f1fe 100644 --- a/samples/HelloWorld/HelloWorld/HelloWorld.DotNet.csproj +++ b/samples/HelloWorld/HelloWorld/HelloWorld.DotNet.csproj @@ -10,4 +10,19 @@ + + + + + + <_AndroidMamMappingFile Include="$(PkgMicrosoft_Intune_MAM_Remapper_Tasks)/content/MonoAndroid10/remapping-config.json" /> + + \ No newline at end of file diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index 93a91c65b78..0dfa99d2683 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Reflection; @@ -259,11 +260,18 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl yield return t; } +#if NET + [RequiresPreviewFeatures] +#endif // NET protected override string? GetSimpleReference (Type type) { string? j = JNIEnv.TypemapManagedToJava (type); if (j != null) { - return j; + return +#if NET + GetReplacementTypeCore (j) ?? +#endif // NET + j; } if (JNIEnv.IsRunningOnDesktop) { return JavaNativeTypeManager.ToJniName (type); @@ -271,17 +279,90 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl return null; } +#if NET + [RequiresPreviewFeatures] +#endif // NET protected override IEnumerable GetSimpleReferences (Type type) { string? j = JNIEnv.TypemapManagedToJava (type); if (j != null) { - yield return j; + yield return +#if NET + GetReplacementTypeCore (j) ?? +#endif // NET + j; } if (JNIEnv.IsRunningOnDesktop) { yield return JavaNativeTypeManager.ToJniName (type); } } +#if NET + [RequiresPreviewFeatures] + protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimpleReference) + { + return new[]{$"{jniSimpleReference}$-CC"}; + } + + [RequiresPreviewFeatures] + protected override string? GetReplacementTypeCore (string jniSimpleReference) + { + if (JNIEnv.ReplacementTypes == null) { + return null; + } + Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: looking for replacement type for `{jniSimpleReference}`"); + Logger.Log (LogLevel.Warn, "*jonp*", new System.Diagnostics.StackTrace (true).ToString ()); + if (JNIEnv.ReplacementTypes.TryGetValue (jniSimpleReference, out var v)) { + return v; + } + return null; + } + + [RequiresPreviewFeatures] + protected override JniRuntime.ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSourceType, string jniMethodName, string jniMethodSignature) + { + if (JNIEnv.ReplacementMethods == null) { + return null; + } + Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: looking for replacement method for (\"{jniSourceType}\", \"{jniMethodName}\", \"{jniMethodSignature}\")"); + if (!JNIEnv.ReplacementMethods.TryGetValue ((jniSourceType, jniMethodName, jniMethodSignature), out var r) && + !JNIEnv.ReplacementMethods.TryGetValue ((jniSourceType, jniMethodName, GetMethodSignatureWithoutReturnType ()), out r) && + !JNIEnv.ReplacementMethods.TryGetValue ((jniSourceType, jniMethodName, null), out r)) { + Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: no method replacement found!"); + return null; + } + var targetSig = r.TargetSignature; + var paramCount = r.ParamCount; + if (targetSig == null && r.TurnStatic) { + targetSig = $"(L{jniSourceType};" + jniMethodSignature.Substring ("(".Length); + paramCount = paramCount ?? JniMemberSignature.GetParameterCountFromMethodSignature (jniMethodSignature); + paramCount++; + } + Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: found replacement: ({GetValue (r.TargetType)}, {GetValue (r.TargetName)}, {GetValue (r.TargetSignature)}, {r.ParamCount?.ToString () ?? "null"}, {r.TurnStatic})"); + return new JniRuntime.ReplacementMethodInfo { + SourceJniType = jniSourceType, + SourceJniMethodName = jniMethodName, + SourceJniMethodSignature = jniMethodSignature, + TargetJniType = r.TargetType ?? jniSourceType, + TargetJniMethodName = r.TargetName ?? jniMethodName, + TargetJniMethodSignature = targetSig ?? jniMethodSignature, + TargetJniMethodParameterCount = paramCount, + TargetJniMethodInstanceToStatic = r.TurnStatic, + }; + + string GetMethodSignatureWithoutReturnType () + { + int i = jniMethodSignature.IndexOf (')'); + return jniMethodSignature.Substring (0, i+1); + } + + string GetValue (string? value) + { + return value == null ? "null" : $"\"{value}\""; + } + } +#endif // NET + delegate Delegate GetCallbackHandler (); static MethodInfo? dynamic_callback_gen; diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index 6df3792fa4b..0c2245b0efc 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -15,6 +15,14 @@ using Java.Interop.Tools.TypeNameMappings; using System.Diagnostics.CodeAnalysis; +#if NET +using ReplacementTypesDict = System.Collections.Generic.Dictionary; +using ReplacementMethodsDict = System.Collections.Generic.Dictionary< + (string SourceType, string SourceName, string? SourceSignature), + (string? TargetType, string? TargetName, string? TargetSignature, int? ParamCount, bool TurnStatic) +>; +#endif // NET + namespace Android.Runtime { #pragma warning disable 0649 struct JnienvInitializeArgs { @@ -35,6 +43,8 @@ struct JnienvInitializeArgs { public int packageNamingPolicy; public byte ioExceptionType; public int jniAddNativeMethodRegistrationAttributePresent; + public IntPtr mappingXml; + public int mappingXmlLen; } #pragma warning restore 0649 @@ -61,6 +71,11 @@ public static partial class JNIEnv { static AndroidRuntime? androidRuntime; static BoundExceptionType BoundExceptionType; +#if NET + internal static ReplacementTypesDict? ReplacementTypes; + internal static ReplacementMethodsDict? ReplacementMethods; +#endif // NET + [ThreadStatic] static byte[]? mvid_bytes; @@ -166,6 +181,14 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) gref_class = args->grefClass; mid_Class_forName = new JniMethodInfo (args->Class_forName, isStatic: true); +#if NET + if (args->mappingXml != IntPtr.Zero) { + var xml = Encoding.UTF8.GetString ((byte*) args->mappingXml, args->mappingXmlLen); + Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: mapping xml: len={args->mappingXmlLen}; {xml}"); + (ReplacementTypes, ReplacementMethods) = MamXmlParser.Parse (xml); + } +#endif // NET + if (args->localRefsAreIndirect == 1) IdentityHash = v => _monodroid_get_identity_hash_code (Handle, v); else diff --git a/src/Mono.Android/Android.Runtime/MamXmlParser.cs b/src/Mono.Android/Android.Runtime/MamXmlParser.cs new file mode 100644 index 00000000000..1a702247626 --- /dev/null +++ b/src/Mono.Android/Android.Runtime/MamXmlParser.cs @@ -0,0 +1,135 @@ +// File must be "stand-alone"; it's included by +// `tools/remap-mam-json-to-xml` + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Xml; + +using ReplacementTypesDict = System.Collections.Generic.Dictionary; +using ReplacementMethodsDict = System.Collections.Generic.Dictionary< + (string SourceType, string SourceName, string? SourceSignature), + (string? TargetType, string? TargetName, string? TargetSignature, int? ParamCount, bool IsStatic) +>; + +namespace Android.Runtime { + + class MamXmlParser { + + public static (ReplacementTypesDict ReplacementTypes, ReplacementMethodsDict ReplacementMethods) Parse (string xml) + { + var replacementTypes = new ReplacementTypesDict (); + var replacementMethods = new ReplacementMethodsDict (); + + using var t = new StringReader (xml); + using var reader = XmlReader.Create (t, new XmlReaderSettings { XmlResolver = null }); + while (reader.Read ()) { + if (reader.NodeType == XmlNodeType.Whitespace || reader.NodeType == XmlNodeType.Comment) { + continue; + } + if (!reader.IsStartElement ()) { + continue; + } + if (!reader.HasAttributes) { + continue; + } + switch (reader.LocalName) { + case "replace-type": + ParseReplaceTypeAttributes (replacementTypes, reader); + break; + case "replace-method": + ParseReplaceMethodAttributes (replacementMethods, reader); + break; + } + } + + return (replacementTypes, replacementMethods); + } + + static void ParseReplaceTypeAttributes (ReplacementTypesDict replacementTypes, XmlReader reader) + { + // + string? from = null; + string? to = null; + while (reader.MoveToNextAttribute ()) { + switch (reader.LocalName) { + case "from": + from = reader.Value; + break; + case "to": + to = reader.Value; + break; + } + } + if (string.IsNullOrEmpty (from) || string.IsNullOrEmpty (to)) { + return; + } + replacementTypes [from] = to; + } + + static void ParseReplaceMethodAttributes (ReplacementMethodsDict replacementMethods, XmlReader reader) + { + // + + string? sourceType = null; + string? sourceMethod = null; + string? sourceMethodSig = null; + string? targetType = null; + string? targetMethod = null; + string? targetMethodSig = null; + int? targetMethodParamCount = null; + bool targetMethodInstanceToStatic = false; + + while (reader.MoveToNextAttribute ()) { + switch (reader.LocalName) { + case "source-type": + sourceType = reader.Value; + break; + case "source-method-name": + sourceMethod = reader.Value; + break; + case "source-method-signature": + sourceMethodSig = reader.Value; + break; + case "target-type": + targetType = reader.Value; + break; + case "target-method-name": + targetMethod = reader.Value; + break; + case "target-method-signature": + targetMethodSig = reader.Value; + break; + case "target-method-parameter-count": + if (int.TryParse (reader.Value, 0, CultureInfo.InvariantCulture, out var v)) { + targetMethodParamCount = v; + } + break; + case "target-method-instance-to-static": + targetMethodInstanceToStatic = reader.Value == "true"; + break; + } + } + if (string.IsNullOrEmpty (sourceType) || string.IsNullOrEmpty (sourceMethod)) { + return; + } + replacementMethods [(sourceType, sourceMethod, sourceMethodSig)] + = (targetType, targetMethod, targetMethodSig, targetMethodParamCount, targetMethodInstanceToStatic); + } + } +} diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index 9e3d8a6c3fd..7bbd5409ea5 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -22,6 +22,7 @@ enable true true + True @@ -253,6 +254,7 @@ + diff --git a/src/Xamarin.Android.Build.Tasks/MamJsonToXml.cs b/src/Xamarin.Android.Build.Tasks/MamJsonToXml.cs new file mode 100644 index 00000000000..2ccd521eaf3 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/MamJsonToXml.cs @@ -0,0 +1,33 @@ +using System; +using System.IO; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.Android.Build.Tasks; + +namespace Xamarin.Android.Tasks +{ + public class MamJsonToXml : AndroidTask + { + public override string TaskPrefix => "A2C"; + + [Required] + public ITaskItem[] MappingFiles { get; set; } + + [Required] + public ITaskItem XmlMappingOutput { get; set; } + + public override bool RunTask () + { + var parser = new MamJsonParser (this.CreateTaskLogger ()); + foreach (var file in MappingFiles) { + parser.Load (file.ItemSpec); + } + var tree = parser.ToXml (); + using (var o = File.CreateText (XmlMappingOutput.ItemSpec)) { + o.WriteLine (tree.ToString ()); + } + return true; + } + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MamJsonParser.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MamJsonParser.cs new file mode 100644 index 00000000000..b817104e455 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MamJsonParser.cs @@ -0,0 +1,215 @@ +// File must be "stand-alone"; it's included by +// `tools/remap-mam-json-to-xml` + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Xml.Linq; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using ReplacementTypesDict = System.Collections.Generic.Dictionary; +using ReplacementMethodsDict = System.Collections.Generic.Dictionary< + (string SourceType, string SourceName, string? SourceSignature), + (string? TargetType, string? TargetName, string? TargetSignature, int? ParamCount, bool IsStatic) +>; + +namespace Xamarin.Android.Tasks +{ + class MamJsonParser + { + Action Logger; + + public readonly ReplacementTypesDict ReplacementTypes = new (); + public readonly ReplacementMethodsDict ReplacementMethods = new (); + + public MamJsonParser (Action logger) + { + Logger = logger; + } + + public void Load (string jsonPath) + { + var json = ReadJson (jsonPath); + + var classRewrites = json["ClassRewrites"]; + if (classRewrites != null) { + ReadClassRewrites (classRewrites); + } + + var globalMethodCalls = json["GlobalMethodCalls"]; + if (globalMethodCalls != null) { + ReadGlobalMethodCalls (globalMethodCalls); + } + } + + public XElement ToXml () + { + return new XElement ("replacements", + GetReplacementTypes (), + GetReplacementMethods ()); + } + + static JObject ReadJson (string path) + { + using (var f = File.OpenText (path)) + using (var r = new JsonTextReader (f)) + return (JObject) JToken.ReadFrom (r); + } + + void ReadClassRewrites (JToken classRewrites) + { + foreach (var classRewrite in classRewrites) { + if (!TryReadClassFromTo (classRewrite, out var from, out var to)) { + Logger (TraceLevel.Verbose, $"No from or to! {classRewrite}"); + continue; + } + ReplacementTypes [from] = to; + var methods = classRewrite ["Methods"]; + if (methods == null) { + continue; + } + foreach (var method in methods) { + var makeStatic = (bool?) method["MakeStatic"] ?? false; + var oldName = (string?) method["OriginalName"]; + var newName = (string?) method["NewName"]; + var oldSig = ReadSignature (method["OriginalParams"]); + if (oldName == null || newName == null) { + continue; + } + ReplacementMethods [(to, oldName, oldSig)] = (to, newName, null, null, makeStatic); + } + } + } + + bool TryReadClassFromTo (JToken token, [NotNullWhen(true)] out string? from, [NotNullWhen(true)] out string? to) + { + from = (string?) token["Class"]?["From"]; + to = (string?) token["Class"]?["To"]; + if (from == null || to == null) { + return false; + } + from = JavaToJniType (from); + to = JavaToJniType (to); + return true; + } + + string? ReadSignature (JToken? token) + { + if (token == null) + return null; + var types = new List (); + foreach (var type in token) { + if (type == null) { + continue; + } + var javaType = ((string?) type) switch { + "boolean" => "Z", + "byte" => "B", + "char" => "C", + "double" => "D", + "float" => "F", + "int" => "I", + "long" => "J", + "short" => "S", + "void" => "V", + var o => JavaToJniTypeSignature (o), + }; + if (javaType == null) { + continue; + } + types.Add (javaType); + } + if (types.Count == 0) + return null; + return "(" + string.Join ("", types) + ")"; + } + + string JavaToJniType (string javaType) + { + return javaType.Replace (".", "/"); + } + + string? JavaToJniTypeSignature (string? javaType) + { + if (javaType == null) { + return null; + } + int arrayCount = 0; + while (javaType.EndsWith ("[]")) { + arrayCount++; + javaType = javaType.Substring(0, javaType.Length - "[]".Length); + } + return new string('[', arrayCount) + "L" + JavaToJniType (javaType) + ";"; + } + + void ReadGlobalMethodCalls (JToken globalMethodCalls) + { + foreach (var globalMethodCall in globalMethodCalls) { + if (!TryReadClassFromTo (globalMethodCall, out var from, out var to)) { + Logger (TraceLevel.Info, $"No from or to! {globalMethodCall}"); + continue; + } + var methods = globalMethodCall ["Methods"]; + if (methods == null) { + continue; + } + foreach (var method in methods) { + var makeStatic = (bool?) method["MakeStatic"] ?? false; + var oldName = (string?) method["OriginalName"]; + var oldSig = ReadSignature (method["OriginalParams"]); + if (oldSig != null) { + throw new Exception ("huh?"); + } + if (oldName == null || oldName.Length < 1) { + continue; + } + var newName = oldName; + ReplacementMethods [(from, oldName, null)] = (to, newName, null, null, makeStatic); + } + } + } + + IEnumerable GetReplacementTypes () + { + foreach (var k in ReplacementTypes.Keys.OrderBy (k => k)) { + yield return new XElement ("replace-type", + new XAttribute ("from", k), + new XAttribute ("to", ReplacementTypes [k])); + } + } + + IEnumerable GetReplacementMethods () + { + var entries = ReplacementMethods.Keys.OrderBy (e => e.SourceType) + .ThenBy (e => e.SourceName) + .ThenBy (e => e.SourceSignature); + foreach (var k in entries) { + var v = ReplacementMethods [k]; + yield return new XElement ("replace-method", + new XAttribute ("source-type", k.SourceType), + new XAttribute ("source-method-name", k.SourceName), + CreateAttribute ("source-method-signature", k.SourceSignature), + CreateAttribute ("target-type", v.TargetType), + CreateAttribute ("target-method-name", v.TargetName), + CreateAttribute ("target-method-signature", v.TargetSignature), + CreateAttribute ("target-method-parameter-count", v.ParamCount.HasValue ? v.ParamCount.Value.ToString () : null), + CreateAttribute ("target-method-instance-to-static", v.IsStatic ? "true" : "false")); + } + } + + XAttribute? CreateAttribute (string name, string? value) + { + if (value == null) { + return null; + } + return new XAttribute (name, value); + } + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 352a9c11cdb..1ebfa151b64 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -68,6 +68,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + @@ -429,6 +430,25 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + + + <_AndroidMamMappingXml>$(MonoAndroidAssetsDirIntermediate)xa-internal/xa-mam-mapping.xml + + + + + + + + diff --git a/src/java-runtime/java-runtime.targets b/src/java-runtime/java-runtime.targets index 4966c24f6e2..812a73516ce 100644 --- a/src/java-runtime/java-runtime.targets +++ b/src/java-runtime/java-runtime.targets @@ -54,7 +54,11 @@ <_AndroidJar>"$(AndroidSdkDirectory)\platforms\android-$(AndroidJavaRuntimeApiLevel)\android.jar" + diff --git a/src/java-runtime/java/mono/android/MonoPackageManager.java b/src/java-runtime/java/mono/android/MonoPackageManager.java index 4a8fac7dbdb..ee696140046 100644 --- a/src/java-runtime/java/mono/android/MonoPackageManager.java +++ b/src/java-runtime/java/mono/android/MonoPackageManager.java @@ -101,6 +101,8 @@ public static void LoadApplication (Context context, ApplicationInfo runtimePack System.loadLibrary("monodroid"); + byte[] mappingXml = getMappingXml (context); + Runtime.initInternal ( language, apks, @@ -108,6 +110,8 @@ public static void LoadApplication (Context context, ApplicationInfo runtimePack appDirs, loader, MonoPackageManager_Resources.Assemblies, + mappingXml, + mappingXml == null ? 0 : mappingXml.length, Build.VERSION.SDK_INT, isEmulator (), haveSplitApks @@ -162,4 +166,31 @@ public static String[] getDependencies () { return MonoPackageManager_Resources.Dependencies; } + + static byte[] getMappingXml (Context context) + { + try { + AssetManager manager = context.getAssets(); + String[] assets = manager.list ("xa-internal"); + if (assets == null) { + return null; + } + for (String asset: assets) { + if (!asset.equals ("xa-mam-mapping.xml")) { + continue; + } + Log.d ("*jonp*", "# jonp: found `xa-mam-mapping.xml`; trying to load..."); + InputStream s = manager.open ("xa-internal/xa-mam-mapping.xml"); + Log.d ("*jonp*", "# jonp: s? " + (s != null)); + byte[] contents = new byte[s.available ()]; + int r = s.read (contents); + Log.d ("*jonp*", "# jonp: read " + r + " bytes from inputstream! expected " + contents.length + "!"); + s.close (); + return contents; + } + } catch (IOException e) { + Log.wtf ("*jonp*", "Error reading `xa-mam-mapping.xml`", e); + } + return null; + } } diff --git a/src/java-runtime/java/mono/android/Runtime.java b/src/java-runtime/java/mono/android/Runtime.java index d40080e4608..2a8c3429012 100644 --- a/src/java-runtime/java/mono/android/Runtime.java +++ b/src/java-runtime/java/mono/android/Runtime.java @@ -15,7 +15,19 @@ public class Runtime { } public static native void init (String lang, String[] runtimeApks, String runtimeDataDir, String[] appDirs, ClassLoader loader, String[] externalStorageDirs, String[] assemblies, String packageName, int apiLevel, String[] environmentVariables); - public static native void initInternal (String lang, String[] runtimeApks, String runtimeDataDir, String[] appDirs, ClassLoader loader, String[] assemblies, int apiLevel, boolean isEmulator, boolean haveSplitApks); + public static native void initInternal ( + String lang, + String[] runtimeApks, + String runtimeDataDir, + String[] appDirs, + ClassLoader loader, + String[] assemblies, + byte[] mappingXml, + int mappingXmlLen, + int apiLevel, + boolean isEmulator, + boolean haveSplitApks + ); public static native void register (String managedType, java.lang.Class nativeClass, String methods); public static native void notifyTimeZoneChanged (); public static native int createNewContext (String[] runtimeApks, String[] assemblies, ClassLoader loader); diff --git a/src/monodroid/jni/mono_android_Runtime.h b/src/monodroid/jni/mono_android_Runtime.h index 38e787d53e7..cefd589332a 100644 --- a/src/monodroid/jni/mono_android_Runtime.h +++ b/src/monodroid/jni/mono_android_Runtime.h @@ -18,10 +18,10 @@ JNIEXPORT void JNICALL Java_mono_android_Runtime_init /* * Class: mono_android_Runtime * Method: initInternal - * Signature: (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/ClassLoader;[Ljava/lang/String;IZ)V + * Signature: (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/ClassLoader;[Ljava/lang/String;[BIIZZ)V */ JNIEXPORT void JNICALL Java_mono_android_Runtime_initInternal -(JNIEnv *, jclass, jstring, jobjectArray, jstring, jobjectArray, jobject, jobjectArray, jint, jboolean, jboolean); + (JNIEnv *, jclass, jstring, jobjectArray, jstring, jobjectArray, jobject, jobjectArray, jbyteArray, jint, jint, jboolean, jboolean); /* * Class: mono_android_Runtime @@ -59,7 +59,7 @@ JNIEXPORT jint JNICALL Java_mono_android_Runtime_createNewContext /* * Class: mono_android_Runtime * Method: createNewContextWithData - * Signature: ([Ljava/lang/String;[Ljava/lang/String;[[BL[Ljava/lang/String;java/lang/ClassLoader;Z)I + * Signature: ([Ljava/lang/String;[Ljava/lang/String;[[B[Ljava/lang/String;Ljava/lang/ClassLoader;Z)I */ JNIEXPORT jint JNICALL Java_mono_android_Runtime_createNewContextWithData (JNIEnv *, jclass, jobjectArray, jobjectArray, jobjectArray, jobjectArray, jobject, jboolean); @@ -79,7 +79,6 @@ JNIEXPORT void JNICALL Java_mono_android_Runtime_switchToContext */ JNIEXPORT void JNICALL Java_mono_android_Runtime_destroyContexts (JNIEnv *, jclass, jintArray); -#endif // ndef ANDROID /* * Class: mono_android_Runtime diff --git a/src/monodroid/jni/monodroid-glue-designer.cc b/src/monodroid/jni/monodroid-glue-designer.cc index 8e8d6ab4cba..cbc6e45a7fb 100644 --- a/src/monodroid/jni/monodroid-glue-designer.cc +++ b/src/monodroid/jni/monodroid-glue-designer.cc @@ -42,7 +42,7 @@ MonodroidRuntime::Java_mono_android_Runtime_createNewContextWithData (JNIEnv *en jstring_array_wrapper runtimeApks (env, runtimeApksJava); jstring_array_wrapper assemblies (env, assembliesJava); jstring_array_wrapper assembliePaths (env, assembliesPaths); - MonoDomain *domain = create_and_initialize_domain (env, klass, runtimeApks, assemblies, assembliesBytes, assembliePaths, loader, /*is_root_domain:*/ false, force_preload_assemblies, /* have_split_apks */ false); + MonoDomain *domain = create_and_initialize_domain (env, klass, runtimeApks, assemblies, assembliesBytes, assembliePaths, loader, nullptr, 0, /*is_root_domain:*/ false, force_preload_assemblies, /* have_split_apks */ false); mono_domain_set (domain, FALSE); int domain_id = mono_domain_get_id (domain); current_context_id = domain_id; diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh index d7c44a23cfb..ed557fe0ab1 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -131,6 +131,8 @@ namespace xamarin::android::internal int packageNamingPolicy; uint8_t boundExceptionType; int jniAddNativeMethodRegistrationAttributePresent; + jbyte* mappingXml; + int mappingXmlLen; }; #if defined (NET) @@ -174,7 +176,8 @@ namespace xamarin::android::internal void Java_mono_android_Runtime_register (JNIEnv *env, jstring managedType, jclass nativeClass, jstring methods); void Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, - jobjectArray assembliesJava, jint apiLevel, jboolean isEmulator, + jobjectArray assembliesJava, jbyteArray mappingXml, jint mappingXmlLen, + jint apiLevel, jboolean isEmulator, jboolean haveSplitApks); #if !defined (ANDROID) jint Java_mono_android_Runtime_createNewContextWithData (JNIEnv *env, jclass klass, jobjectArray runtimeApksJava, jobjectArray assembliesJava, @@ -299,9 +302,9 @@ namespace xamarin::android::internal void parse_gdb_options (); void mono_runtime_init (dynamic_local_string& runtime_args); #if defined (NET) - void init_android_runtime (JNIEnv *env, jclass runtimeClass, jobject loader); + void init_android_runtime (JNIEnv *env, jclass runtimeClass, jobject loader, jbyteArray mappingXml, jint mappingXmlLen); #else //def NET - void init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobject loader); + void init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobject loader, jbyteArray mappingXml, jint mappingXmlLen); void setup_bundled_app (const char *dso_name); #endif // ndef NET void set_environment_variable_for_directory (const char *name, jstring_wrapper &value, bool createDirectory, mode_t mode); @@ -326,7 +329,7 @@ namespace xamarin::android::internal MonoDomain* create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks, bool is_root_domain, bool have_split_apks); MonoDomain* create_and_initialize_domain (JNIEnv* env, jclass runtimeClass, jstring_array_wrapper &runtimeApks, jstring_array_wrapper &assemblies, jobjectArray assembliesBytes, jstring_array_wrapper &assembliesPaths, - jobject loader, bool is_root_domain, bool force_preload_assemblies, + jobject loader, jbyteArray mappingXml, jint mappingXmlLen, bool is_root_domain, bool force_preload_assemblies, bool have_split_apks); void gather_bundled_assemblies (jstring_array_wrapper &runtimeApks, size_t *out_user_assemblies_count, bool have_split_apks); diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index cc1eb942dc2..9e25779742a 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -1050,7 +1050,7 @@ MonodroidRuntime::init_android_runtime ( #if !defined (NET) MonoDomain *domain, #endif // ndef NET - JNIEnv *env, jclass runtimeClass, jobject loader) + JNIEnv *env, jclass runtimeClass, jobject loader, jbyteArray mappingXml, jint mappingXmlLen) { constexpr char icall_typemap_java_to_managed[] = "Java.Interop.TypeManager::monodroid_typemap_java_to_managed"; constexpr char icall_typemap_managed_to_java[] = "Android.Runtime.JNIEnv::monodroid_typemap_managed_to_java"; @@ -1192,6 +1192,12 @@ MonodroidRuntime::init_android_runtime ( native_to_managed_index = internal_timing->start_event (TimingEventKind::NativeToManagedTransition); } + if (mappingXml != nullptr) { + init.mappingXml = env->GetByteArrayElements (mappingXml, nullptr); + init.mappingXmlLen = mappingXmlLen; + } + log_warn (LOG_DEFAULT, "# jonp: mappingXml? len=%i, xml=%p", init.mappingXmlLen, init.mappingXml); + #if defined (NET) && defined (ANDROID) MonoError error; auto initialize = reinterpret_cast (mono_method_get_unmanaged_callers_only_ftnptr (method, &error)); @@ -1205,6 +1211,10 @@ MonodroidRuntime::init_android_runtime ( utils.monodroid_runtime_invoke (domain, method, nullptr, args, nullptr); #endif // ndef NET && ndef ANDROID + if (init.mappingXml != nullptr) { + env->ReleaseByteArrayElements (mappingXml, init.mappingXml, JNI_ABORT); + } + if (XA_UNLIKELY (FastTiming::enabled ())) { internal_timing->end_event (native_to_managed_index); } @@ -1959,7 +1969,7 @@ monodroid_Mono_UnhandledException_internal ([[maybe_unused]] MonoException *ex) MonoDomain* MonodroidRuntime::create_and_initialize_domain (JNIEnv* env, jclass runtimeClass, jstring_array_wrapper &runtimeApks, jstring_array_wrapper &assemblies, [[maybe_unused]] jobjectArray assembliesBytes, - [[maybe_unused]] jstring_array_wrapper &assembliesPaths, jobject loader, bool is_root_domain, + [[maybe_unused]] jstring_array_wrapper &assembliesPaths, jobject loader, jbyteArray mappingXml, jint mappingXmlLen, bool is_root_domain, bool force_preload_assemblies, bool have_split_apks) { MonoDomain* domain = create_domain (env, runtimeApks, is_root_domain, have_split_apks); @@ -1987,10 +1997,10 @@ MonodroidRuntime::create_and_initialize_domain (JNIEnv* env, jclass runtimeClass #if defined (NET) load_assemblies (default_alc, preload, assemblies); - init_android_runtime (env, runtimeClass, loader); + init_android_runtime (env, runtimeClass, loader, mappingXml, mappingXmlLen); #else // def NET load_assemblies (domain, preload, assemblies); - init_android_runtime (domain, env, runtimeClass, loader); + init_android_runtime (domain, env, runtimeClass, loader, mappingXml, mappingXmlLen); #endif // ndef NET osBridge.add_monodroid_domain (domain); @@ -2150,7 +2160,7 @@ MonodroidRuntime::install_logging_handlers () inline void MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, - jobjectArray assembliesJava, jint apiLevel, jboolean isEmulator, + jobjectArray assembliesJava, jbyteArray mappingXml, jint mappingXmlLen, jint apiLevel, jboolean isEmulator, jboolean haveSplitApks) { char *mono_log_mask_raw = nullptr; @@ -2363,7 +2373,7 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl jstring_array_wrapper assemblies (env, assembliesJava); jstring_array_wrapper assembliesPaths (env); /* the first assembly is used to initialize the AppDomain name */ - create_and_initialize_domain (env, klass, runtimeApks, assemblies, nullptr, assembliesPaths, loader, /*is_root_domain:*/ true, /*force_preload_assemblies:*/ false, haveSplitApks); + create_and_initialize_domain (env, klass, runtimeApks, assemblies, nullptr, assembliesPaths, loader, mappingXml, mappingXmlLen, /*is_root_domain:*/ true, /*force_preload_assemblies:*/ false, haveSplitApks); #if defined (ANDROID) && !defined (NET) // Mono from mono/mono has a bug which requires us to install the handlers after `mono_init_jit_version` is called @@ -2439,7 +2449,7 @@ JNI_OnLoad (JavaVM *vm, void *reserved) JNIEXPORT void JNICALL Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, - [[maybe_unused]] jobjectArray externalStorageDirs, jobjectArray assembliesJava, [[maybe_unused]] jstring packageName, + [[maybe_unused]] jobjectArray externalStorageDirs, jobjectArray assembliesJava, jbyteArray mappingXml, jint mappingXmlLen, [[maybe_unused]] jstring packageName, jint apiLevel, [[maybe_unused]] jobjectArray environmentVariables) { monodroidRuntime.Java_mono_android_Runtime_initInternal ( @@ -2451,6 +2461,8 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject appDirs, loader, assembliesJava, + mappingXml, + mappingXmlLen, apiLevel, /* isEmulator */ JNI_FALSE, /* haveSplitApks */ JNI_FALSE @@ -2460,7 +2472,7 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject JNIEXPORT void JNICALL Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, - jobjectArray assembliesJava, jint apiLevel, jboolean isEmulator, + jobjectArray assembliesJava, jbyteArray mappingXml, jint mappingXmlLen, jint apiLevel, jboolean isEmulator, jboolean haveSplitApks) { monodroidRuntime.Java_mono_android_Runtime_initInternal ( @@ -2472,6 +2484,8 @@ Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, appDirs, loader, assembliesJava, + mappingXml, + mappingXmlLen, apiLevel, isEmulator, haveSplitApks diff --git a/src/monodroid/monodroid.csproj b/src/monodroid/monodroid.csproj index e7364786d44..d6bf5ead80f 100644 --- a/src/monodroid/monodroid.csproj +++ b/src/monodroid/monodroid.csproj @@ -16,6 +16,7 @@ + \ No newline at end of file diff --git a/samples/HelloWorld/HelloWorld/remap.xml b/samples/HelloWorld/HelloWorld/remap.xml new file mode 100644 index 00000000000..8363c159771 --- /dev/null +++ b/samples/HelloWorld/HelloWorld/remap.xml @@ -0,0 +1,3 @@ + + + diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index 9a75c756975..91088f8ac55 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -313,6 +313,7 @@ protected override IEnumerable GetSimpleReferences (Type type) Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: looking for replacement type for `{jniSimpleReference}`"); Logger.Log (LogLevel.Warn, "*jonp*", new System.Diagnostics.StackTrace (true).ToString ()); if (JNIEnv.ReplacementTypes.TryGetValue (jniSimpleReference, out var v)) { + Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: found replacement type: `{jniSimpleReference}` => `{v}`"); return v; } return null; diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index 7bbd5409ea5..da86b4581dc 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -22,7 +22,6 @@ enable true true - True diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/MergeRemapXml.cs b/src/Xamarin.Android.Build.Tasks/Tasks/MergeRemapXml.cs new file mode 100644 index 00000000000..c929a94a0db --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tasks/MergeRemapXml.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml; +using System.Text; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +using Microsoft.Android.Build.Tasks; + +namespace Xamarin.Android.Tasks +{ + public class MergeRemapXml : AndroidTask + { + public override string TaskPrefix => "MRX"; + + public ITaskItem[] InputRemapXmlFiles { get; set; } + + [Required] + public ITaskItem OutputFile { get; set; } + + public override bool RunTask () + { + Directory.CreateDirectory (Path.GetDirectoryName (OutputFile.ItemSpec)); + + var settings = new XmlWriterSettings () { + Encoding = new UTF8Encoding (false), + Indent = true, + OmitXmlDeclaration = true, + }; + using var output = new StreamWriter (OutputFile.ItemSpec, append: false, encoding: settings.Encoding); + using (var writer = XmlWriter.Create (output, settings)) { + writer.WriteStartElement ("replacements"); + var seen = new HashSet (StringComparer.OrdinalIgnoreCase); + foreach (var file in InputRemapXmlFiles) { + if (!seen.Add (file.ItemSpec)) { + continue; + } + MergeInputFile (writer, file.ItemSpec); + } + writer.WriteEndElement (); + } + output.WriteLine (); + return !Log.HasLoggedErrors; + } + + void MergeInputFile (XmlWriter writer, string file) + { + if (!File.Exists (file)) { + Log.LogWarning ($"Specified input file `{file}` does not exist. Ignoring."); + return; + } + var settings = new XmlReaderSettings { + XmlResolver = null, + }; + try { + using var reader = XmlReader.Create (File.OpenRead (file), settings); + if (reader.MoveToContent () != XmlNodeType.Element) { + return; + } + if (reader.LocalName != "replacements") { + Log.LogWarning ($"Input file `{file}` does not start with ``. Skipping."); + return; + } + while (reader.Read ()) { + if (reader.NodeType != XmlNodeType.Element) { + continue; + } + writer.WriteNode (reader, defattr: true); + } + } + catch (Exception e) { + Log.LogWarning ($"Input file `{file}` could not be read: {e.Message} Skipping."); + Log.LogDebugMessage ($"Input file `{file}` could not be read: {e.ToString ()}"); + } + } + } +} + diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 1ebfa151b64..cab6fe75512 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -68,6 +68,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. + @@ -403,7 +404,8 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. - + + + + - - <_AndroidMamMappingXml>$(MonoAndroidAssetsDirIntermediate)xa-internal/xa-mam-mapping.xml - - - + + + - + <_AndroidRemapMembers Include="$(MonoAndroidAssetsDirIntermediate)xa-internal/xa-mam-mapping.xml" /> + + + + + + + + + + + diff --git a/src/java-runtime/java/mono/android/MonoPackageManager.java b/src/java-runtime/java/mono/android/MonoPackageManager.java index ee696140046..2903d63fbe9 100644 --- a/src/java-runtime/java/mono/android/MonoPackageManager.java +++ b/src/java-runtime/java/mono/android/MonoPackageManager.java @@ -176,20 +176,20 @@ static byte[] getMappingXml (Context context) return null; } for (String asset: assets) { - if (!asset.equals ("xa-mam-mapping.xml")) { + if (!asset.equals ("xa-remap-members.xml")) { continue; } - Log.d ("*jonp*", "# jonp: found `xa-mam-mapping.xml`; trying to load..."); - InputStream s = manager.open ("xa-internal/xa-mam-mapping.xml"); - Log.d ("*jonp*", "# jonp: s? " + (s != null)); - byte[] contents = new byte[s.available ()]; - int r = s.read (contents); - Log.d ("*jonp*", "# jonp: read " + r + " bytes from inputstream! expected " + contents.length + "!"); - s.close (); - return contents; + try (InputStream s = manager.open ("xa-internal/xa-remap-members.xml")) { + byte[] contents = new byte[s.available ()]; + int r = s.read (contents); + if (r != -1 && r != contents.length) { + Log.w ("monodroid", "Only read " + r + " bytes, not the expected " + contents.length + " bytes!"); + } + return contents; + } } } catch (IOException e) { - Log.wtf ("*jonp*", "Error reading `xa-mam-mapping.xml`", e); + Log.wtf ("monodroid", "Error reading `xa-internal/xa-remap-members.xml`", e); } return null; } From 4015a41b80c5e4400da6c21ce9f37aa413b9ee7c Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Thu, 12 May 2022 19:40:42 -0400 Subject: [PATCH 06/16] Sample [to remove after moving into a unit test] Use `@(AndroidJavaSource)` to add a pair of Java types: public class RemapActivity extends Activity { public void onMyCreate (android.os.Bundle bundle); } class ViewHelper { public static void mySetOnClickListener (android.view.View view, android.view.View.OnClickListener listener); } Update `remap.xml` to: 1. Rename `android.app.Activity` to `example.RemapActivity` 2. Rename "`Activity.onCreate()`" to `RemapActivity.onMyCreate()` 3. "Patch" `View.setOnClickListener()` to instead call `ViewHelper.mySetOnClickListener()`. "Learnings" from this exercise: 1. Changing the Java hierarchy "requires" changing the managed hierarchy to mirror it. If we rename `Activity` to `RemapActivity` but *don't* change `MainActivity` to inherit the (bound!) `Example.RemapActivity`, the app *crashes*: JNI DETECTED ERROR IN APPLICATION: can't call void example.RemapActivity.onMyCreate(android.os.Bundle) on instance of example.MainActivity This can be "fixed" *without* changing the base class of `MainActivity` by instead changing the base class of the Java Callable Wrapper for `MainActivity` to `example.RemapActivity`. This can be done manually, but isn't really supported in "normal" xamarin-android usage. Presumably InTune would make this Just Work by e.g. patching the `MainActivity.class` file. 2. Note the "scare quotes" in (2): you *don't* rename `Activity.onCreate()`, because after renaming `Activity` to `example.RemapActivity`, `Activity` *no longer exists*. This warps the brain a bit. You can rename methods, but the `/replace-method/@source-type` value needs to be the `/replace-type/@to` value. This: not this: While it would be more intuitive to support the latter, I couldn't figure out how to make it work robustly and efficiently. 3. Don't intermix type renames with `/replace-method]@target-method-instance-to-static='true']`. It *can* be done, but also warps the brain. The deal with `@target-method-instance-to-static` is that it it changes the target method signature -- unless explicitly provided in `/replace-method/@target-method-signature` -- so that the "source declaring type" is a prefix. Thus given we'll look for `ViewHelper.mySetOnClickListener(View, View.OnClickListener)`. If we renamed `View` to `MyView`, we would instead look for `ViewHelper.mySetOnClickListener(MyView, View.OnClickListener)` (note changed parameter type). This almost certainly *won't* work right for many circumstances. --- external/Java.Interop | 2 +- samples/HelloWorld/HelloWorld/MainActivity.cs | 7 ++++++- .../HelloWorld/HelloWorld/RemapActivity.java | 17 +++++++++++++++++ samples/HelloWorld/HelloWorld/remap.xml | 12 +++++++++++- .../Android.Runtime/AndroidRuntime.cs | 17 +++++++++++++---- src/Mono.Android/Android.Runtime/JNIEnv.cs | 2 +- src/monodroid/jni/mono_android_Runtime.h | 17 ++++++++--------- src/monodroid/jni/monodroid-glue.cc | 8 ++++---- 8 files changed, 61 insertions(+), 21 deletions(-) create mode 100644 samples/HelloWorld/HelloWorld/RemapActivity.java diff --git a/external/Java.Interop b/external/Java.Interop index 7699359dbcc..bce69180b90 160000 --- a/external/Java.Interop +++ b/external/Java.Interop @@ -1 +1 @@ -Subproject commit 7699359dbccbee7c7327ce9cb309ab7ca5adda83 +Subproject commit bce69180b90f14c71c2eb1a7b5506077057e5bb7 diff --git a/samples/HelloWorld/HelloWorld/MainActivity.cs b/samples/HelloWorld/HelloWorld/MainActivity.cs index 43d1421ef7f..c29749e51a6 100644 --- a/samples/HelloWorld/HelloWorld/MainActivity.cs +++ b/samples/HelloWorld/HelloWorld/MainActivity.cs @@ -9,7 +9,12 @@ namespace HelloWorld Label = "HelloWorld", MainLauncher = true, Name = "example.MainActivity")] - public class MainActivity : Activity + public class MainActivity : +#if NET + Example.RemapActivity +#else // NET + Activity +#endif // NET { int count = 1; diff --git a/samples/HelloWorld/HelloWorld/RemapActivity.java b/samples/HelloWorld/HelloWorld/RemapActivity.java new file mode 100644 index 00000000000..c1672a609b1 --- /dev/null +++ b/samples/HelloWorld/HelloWorld/RemapActivity.java @@ -0,0 +1,17 @@ +package example; + +import android.util.Log; + +public class RemapActivity extends android.app.Activity { + public void onMyCreate (android.os.Bundle bundle) { + Log.d ("*jonp*", "RemapActivity.onMyCreate() invoked!"); + super.onCreate(bundle); + } +} + +class ViewHelper { + public static void mySetOnClickListener (android.view.View view, android.view.View.OnClickListener listener) { + Log.d ("*jonp*", "ViewHelper.mySetOnClickListener() invoked!"); + view.setOnClickListener (listener); + } +} diff --git a/samples/HelloWorld/HelloWorld/remap.xml b/samples/HelloWorld/HelloWorld/remap.xml index 8363c159771..283a3557045 100644 --- a/samples/HelloWorld/HelloWorld/remap.xml +++ b/samples/HelloWorld/HelloWorld/remap.xml @@ -1,3 +1,13 @@ - + + + diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index 91088f8ac55..f35002c7278 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -341,16 +341,25 @@ protected override IEnumerable GetSimpleReferences (Type type) Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: targetType={targetType.ToString ()}, targetName={targetName.ToString ()}, targetSig=`{targetSig.ToString()}`, paramCount={paramCountStr.ToString ()}, isStatic={isStaticStr.ToString ()}"); - int paramCount = 0; - if (!paramCountStr.IsEmpty && - !int.TryParse (paramCountStr, 0, System.Globalization.CultureInfo.InvariantCulture, out paramCount)) { - return null; + int? paramCount = null; + if (!paramCountStr.IsEmpty) { + if (!int.TryParse (paramCountStr, 0, System.Globalization.CultureInfo.InvariantCulture, out var count)) { + return null; + } + paramCount = count; } + bool isStatic = false; if (isStaticStr.Equals ("true", StringComparison.Ordinal)) { isStatic = true; } + if (targetSig.IsEmpty && isStatic) { + paramCount = paramCount ?? JniMemberSignature.GetParameterCountFromMethodSignature (jniMethodSignature); + paramCount++; + jniMethodSignature = $"(L{jniSourceType};" + jniMethodSignature.Substring ("(".Length); + } + return new JniRuntime.ReplacementMethodInfo { SourceJniType = jniSourceType, SourceJniMethodName = jniMethodName, diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index 98a18d98249..aca983580bf 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -182,7 +182,7 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) if (args->mappingXml != IntPtr.Zero) { var xml = Encoding.UTF8.GetString ((byte*) args->mappingXml, args->mappingXmlLen); Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: mapping xml: len={args->mappingXmlLen}; {xml}"); - (ReplacementTypes, ReplacementMethods) = MamXmlParser.Parse (xml); + (ReplacementTypes, ReplacementMethods) = MamXmlParser.ParseStrings (xml); } #endif // NET diff --git a/src/monodroid/jni/mono_android_Runtime.h b/src/monodroid/jni/mono_android_Runtime.h index cefd589332a..2dfa0a6cf00 100644 --- a/src/monodroid/jni/mono_android_Runtime.h +++ b/src/monodroid/jni/mono_android_Runtime.h @@ -39,15 +39,6 @@ JNIEXPORT void JNICALL Java_mono_android_Runtime_register JNIEXPORT void JNICALL Java_mono_android_Runtime_notifyTimeZoneChanged (JNIEnv *, jclass); -/* - * Class: mono_android_Runtime - * Method: dumpTimingData - * Signature: ()V -*/ -JNIEXPORT void JNICALL Java_mono_android_Runtime_dumpTimingData - (JNIEnv *, jclass); - -#if !defined (ANDROID) /* * Class: mono_android_Runtime * Method: createNewContext @@ -88,6 +79,14 @@ JNIEXPORT void JNICALL Java_mono_android_Runtime_destroyContexts JNIEXPORT void JNICALL Java_mono_android_Runtime_propagateUncaughtException (JNIEnv *, jclass, jobject, jthrowable); +/* + * Class: mono_android_Runtime + * Method: dumpTimingData + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_mono_android_Runtime_dumpTimingData + (JNIEnv *, jclass); + #ifdef __cplusplus } #endif diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 9e25779742a..3247bd89784 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -1192,7 +1192,7 @@ MonodroidRuntime::init_android_runtime ( native_to_managed_index = internal_timing->start_event (TimingEventKind::NativeToManagedTransition); } - if (mappingXml != nullptr) { + if (mappingXml != nullptr && mappingXmlLen > 0) { init.mappingXml = env->GetByteArrayElements (mappingXml, nullptr); init.mappingXmlLen = mappingXmlLen; } @@ -2449,7 +2449,7 @@ JNI_OnLoad (JavaVM *vm, void *reserved) JNIEXPORT void JNICALL Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, - [[maybe_unused]] jobjectArray externalStorageDirs, jobjectArray assembliesJava, jbyteArray mappingXml, jint mappingXmlLen, [[maybe_unused]] jstring packageName, + [[maybe_unused]] jobjectArray externalStorageDirs, jobjectArray assembliesJava, [[maybe_unused]] jstring packageName, jint apiLevel, [[maybe_unused]] jobjectArray environmentVariables) { monodroidRuntime.Java_mono_android_Runtime_initInternal ( @@ -2461,8 +2461,8 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject appDirs, loader, assembliesJava, - mappingXml, - mappingXmlLen, + /* mappingXml */ nullptr, + /* mappingXmlLen */ 0, apiLevel, /* isEmulator */ JNI_FALSE, /* haveSplitApks */ JNI_FALSE From cb1edc48e71bd9bc22027f92630e46d10f6b46fb Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Mon, 16 May 2022 20:35:24 -0400 Subject: [PATCH 07/16] Turn HelloWorld into an actual unit test. --- .../Resources/RemapActivity.java | 17 +++++ .../Utilities/ResourceData.cs | 2 + .../Android/AndroidBuildActions.cs | 1 + .../Android/AndroidItem.cs | 11 ++++ .../Tests/XASdkDeployTests.cs | 65 +++++++++++++++++++ 5 files changed, 96 insertions(+) create mode 100644 src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Resources/RemapActivity.java diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Resources/RemapActivity.java b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Resources/RemapActivity.java new file mode 100644 index 00000000000..2be2dc9b667 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Resources/RemapActivity.java @@ -0,0 +1,17 @@ +package example; + +import android.util.Log; + +public class RemapActivity extends android.app.Activity { + public void onMyCreate (android.os.Bundle bundle) { + Log.d ("*REMAP-TEST*", "RemapActivity.onMyCreate() invoked!"); + super.onCreate(bundle); + } +} + +class ViewHelper { + public static void mySetOnClickListener (android.view.View view, android.view.View.OnClickListener listener) { + Log.d ("*REMAP-TEST*", "ViewHelper.mySetOnClickListener() invoked!"); + view.setOnClickListener (listener); + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ResourceData.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ResourceData.cs index 163241116fe..81cce87c3ea 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ResourceData.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ResourceData.cs @@ -18,6 +18,7 @@ static class ResourceData static Lazy apacheHttpClient_cs = new Lazy (() => GetResourceData ("ApacheHttpClient.cs")); static Lazy javadocCopyright = new Lazy (() => GetResourceData ("javadoc-copyright.xml")); static Lazy javaSourceTestExtension = new Lazy (() => GetResourceData ("JavaSourceTestExtension.java")); + static Lazy remapActivity = new Lazy (() => GetResourceData ("RemapActivity.java")); public static byte[] JavaSourceJarTestJar => javaSourceJarTestJar.Value; public static byte[] JavaSourceJarTestSourcesJar => javaSourceJarTestSourcesJar.Value; @@ -28,6 +29,7 @@ static class ResourceData public static byte [] JavadocCopyright => javadocCopyright.Value; public static string JavaSourceTestExtension => Encoding.ASCII.GetString (javaSourceTestExtension.Value); + public static string RemapActivity => Encoding.UTF8.GetString (remapActivity.Value); static byte[] GetResourceData (string name) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/AndroidBuildActions.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/AndroidBuildActions.cs index 513993ed100..965a0166f2f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/AndroidBuildActions.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/AndroidBuildActions.cs @@ -22,6 +22,7 @@ public static class AndroidBuildActions public const string AndroidLibrary = "AndroidLibrary"; public const string AndroidLintConfig = "AndroidLintConfig"; public const string AndroidNativeLibrary = "AndroidNativeLibrary"; + public const string _AndroidRemapMembers = "_AndroidRemapMembers"; public const string ProguardConfiguration = "ProguardConfiguration"; public const string TransformFile = "TransformFile"; public const string InputJar = "InputJar"; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/AndroidItem.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/AndroidItem.cs index a375ebc4cea..15c2b7ea4b0 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/AndroidItem.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/AndroidItem.cs @@ -59,6 +59,17 @@ public AndroidLintConfig (Func include) { } } + public class _AndroidRemapMembers : BuildItem + { + public _AndroidRemapMembers (string include) + : this (() => include) + { + } + public _AndroidRemapMembers (Func include) + : base (AndroidBuildActions._AndroidRemapMembers, include) + { + } + } public class EmbeddedJar : BuildItem { public EmbeddedJar (string include) diff --git a/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs b/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs index eea9c3c211d..dfd2d8ff207 100644 --- a/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using System.Reflection; +using System.Text; using System.Threading; using Mono.Debugging.Client; using Mono.Debugging.Soft; @@ -156,6 +157,70 @@ public void DotNetDebug () WaitFor (2000); Assert.IsTrue (breakpointHit, "Should have a breakpoint"); } + + [Test] + public void TypeAndMemberRemapping ([Values (false, true)] bool isRelease) + { + AssertHasDevices (); + + var proj = new XASdkProject () { + IsRelease = isRelease, + OtherBuildItems = { + new AndroidItem._AndroidRemapMembers ("remap.xml") { + TextContent = () => @" + + + + + +", + }, + new AndroidItem.AndroidJavaSource ("RemapActivity.java") { + Encoding = Encoding.UTF8, + TextContent = () => ResourceData.RemapActivity, + Metadata = { + { "Bind", "True" }, + }, + }, + }, + }; + proj.MainActivity = proj.DefaultMainActivity.Replace (": Activity", "global::Example.RemapActivity"); + proj.SetRuntimeIdentifier (DeviceAbi); + var relativeProjDir = Path.Combine ("temp", TestName); + var fullProjDir = Path.Combine (Root, relativeProjDir); + TestOutputDirectories [TestContext.CurrentContext.Test.ID] = fullProjDir; + var files = proj.Save (); + proj.Populate (relativeProjDir, files); + proj.CopyNuGetConfig (relativeProjDir); + var dotnet = new DotNetCLI (proj, Path.Combine (fullProjDir, proj.ProjectFilePath)); + + Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed"); + Assert.IsTrue (dotnet.Run (), "`dotnet run` should succeed"); + + bool didLaunch = WaitForActivityToStart (proj.PackageName, "MainActivity", + Path.Combine (fullProjDir, "logcat.log"), 30); + Assert.IsTrue (didLaunch, "MainActivity should have launched!"); + var logcatOutput = File.ReadAllText (Path.Combine (fullProjDir, "logcat.log")); + + StringAssert.Contains ( + "RemapActivity.onMyCreate() invoked!", + logcatOutput, + "Activity.onCreate() wasn't remapped to RemapActivity.onMyCreate()!" + ); + StringAssert.Contains ( + "ViewHelper.mySetOnClickListener() invoked!", + logcatOutput, + "View.setOnClickListener() wasn't remapped to ViewHelper.mySetOnClickListener()!" + ); + } } } #endif From 04daf4711c36b6616d7c0a14ac48f12b3eee61c1 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Tue, 17 May 2022 06:27:01 -0400 Subject: [PATCH 08/16] Update *.apkdesc files. Otherwise update unit tests. --- Documentation/workflow/DevelopmentTips.md | 12 +++++++++ .../Utilities/ResourceData.cs | 9 +++++-- .../MSBuildDeviceIntegration.csproj | 5 ++++ .../Resources/RemapActivity.java | 0 .../Resources/RemapActivity.xml | 13 +++++++++ .../Tests/XASdkDeployTests.cs | 27 +++++-------------- .../Mono.Android.NET-Tests.csproj | 4 +++ .../Runtime-Microsoft.Android.Sdk/Remaps.xml | 21 +++++++++++++++ 8 files changed, 69 insertions(+), 22 deletions(-) rename {src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests => tests/MSBuildDeviceIntegration}/Resources/RemapActivity.java (100%) create mode 100644 tests/MSBuildDeviceIntegration/Resources/RemapActivity.xml create mode 100644 tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Remaps.xml diff --git a/Documentation/workflow/DevelopmentTips.md b/Documentation/workflow/DevelopmentTips.md index 52077bcc67f..68f96dc1ac0 100644 --- a/Documentation/workflow/DevelopmentTips.md +++ b/Documentation/workflow/DevelopmentTips.md @@ -2,6 +2,18 @@ Tips and tricks while developing Xamarin.Android. +# Run MSBuild-Based On-Device Unit Tests + +The [`tests/MSBuildDeviceIntegration`](tests/MSBuildDeviceIntegration) +directory contains NUnit-based unit tests which need to run against an attached +Android device (hardware or emulator). There are *lots* of tests in here, and +running them all can take a significant amount of time. + +If you need to run only *one* `[Test]` method, you can use +[`dotnet test --filter`](https://docs.microsoft.com/dotnet/core/testing/selective-unit-tests?pivots=mstest): + + ./dotnet-local.sh test bin/TestDebug/MSBuildDeviceIntegration/net6.0/MSBuildDeviceIntegration.dll --filter "Name~TypeAndMemberRemapping" + # Update directory When a Xamarin.Android app launches on an Android device, and the app was diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ResourceData.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ResourceData.cs index 81cce87c3ea..198e5084e2e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ResourceData.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/ResourceData.cs @@ -18,7 +18,8 @@ static class ResourceData static Lazy apacheHttpClient_cs = new Lazy (() => GetResourceData ("ApacheHttpClient.cs")); static Lazy javadocCopyright = new Lazy (() => GetResourceData ("javadoc-copyright.xml")); static Lazy javaSourceTestExtension = new Lazy (() => GetResourceData ("JavaSourceTestExtension.java")); - static Lazy remapActivity = new Lazy (() => GetResourceData ("RemapActivity.java")); + static Lazy remapActivityJava = new Lazy (() => GetResourceData ("RemapActivity.java")); + static Lazy remapActivityXml = new Lazy (() => GetResourceData ("RemapActivity.xml")); public static byte[] JavaSourceJarTestJar => javaSourceJarTestJar.Value; public static byte[] JavaSourceJarTestSourcesJar => javaSourceJarTestSourcesJar.Value; @@ -29,11 +30,15 @@ static class ResourceData public static byte [] JavadocCopyright => javadocCopyright.Value; public static string JavaSourceTestExtension => Encoding.ASCII.GetString (javaSourceTestExtension.Value); - public static string RemapActivity => Encoding.UTF8.GetString (remapActivity.Value); + public static string RemapActivityJava => Encoding.UTF8.GetString (remapActivityJava.Value); + public static string RemapActivityXml => Encoding.UTF8.GetString (remapActivityXml.Value); static byte[] GetResourceData (string name) { using var s = typeof (ResourceData).Assembly.GetManifestResourceStream (name); + if (s == null) { + throw new NotSupportedException ($"Could not find resource `{name}` in assembly `{typeof (ResourceData).Assembly}`!"); + } using var m = new MemoryStream (checked ((int) s.Length)); s.CopyTo (m); return m.ToArray (); diff --git a/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj b/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj index 3fe8451ca3f..8ad46ed085c 100644 --- a/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj +++ b/tests/MSBuildDeviceIntegration/MSBuildDeviceIntegration.csproj @@ -27,6 +27,11 @@ + + + %(FileName)%(Extension) + + diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Resources/RemapActivity.java b/tests/MSBuildDeviceIntegration/Resources/RemapActivity.java similarity index 100% rename from src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Resources/RemapActivity.java rename to tests/MSBuildDeviceIntegration/Resources/RemapActivity.java diff --git a/tests/MSBuildDeviceIntegration/Resources/RemapActivity.xml b/tests/MSBuildDeviceIntegration/Resources/RemapActivity.xml new file mode 100644 index 00000000000..283a3557045 --- /dev/null +++ b/tests/MSBuildDeviceIntegration/Resources/RemapActivity.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs b/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs index dfd2d8ff207..68c5f1898e3 100644 --- a/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs @@ -166,33 +166,20 @@ public void TypeAndMemberRemapping ([Values (false, true)] bool isRelease) var proj = new XASdkProject () { IsRelease = isRelease, OtherBuildItems = { - new AndroidItem._AndroidRemapMembers ("remap.xml") { - TextContent = () => @" - - - - - -", + new AndroidItem._AndroidRemapMembers ("RemapActivity.xml") { + Encoding = Encoding.UTF8, + TextContent = () => ResourceData.RemapActivityXml, }, new AndroidItem.AndroidJavaSource ("RemapActivity.java") { - Encoding = Encoding.UTF8, - TextContent = () => ResourceData.RemapActivity, + Encoding = new UTF8Encoding (encoderShouldEmitUTF8Identifier: false), + TextContent = () => ResourceData.RemapActivityJava, Metadata = { { "Bind", "True" }, }, }, }, }; - proj.MainActivity = proj.DefaultMainActivity.Replace (": Activity", "global::Example.RemapActivity"); + proj.MainActivity = proj.DefaultMainActivity.Replace (": Activity", ": global::Example.RemapActivity"); proj.SetRuntimeIdentifier (DeviceAbi); var relativeProjDir = Path.Combine ("temp", TestName); var fullProjDir = Path.Combine (Root, relativeProjDir); @@ -206,7 +193,7 @@ public void TypeAndMemberRemapping ([Values (false, true)] bool isRelease) Assert.IsTrue (dotnet.Run (), "`dotnet run` should succeed"); bool didLaunch = WaitForActivityToStart (proj.PackageName, "MainActivity", - Path.Combine (fullProjDir, "logcat.log"), 30); + Path.Combine (fullProjDir, "logcat.log")); Assert.IsTrue (didLaunch, "MainActivity should have launched!"); var logcatOutput = File.ReadAllText (Path.Combine (fullProjDir, "logcat.log")); diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj index 5cf15cbadda..4bf8b2ef988 100644 --- a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj @@ -33,6 +33,10 @@ r8 + + <_AndroidRemapMembers Include="Remaps.xml" /> + + diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Remaps.xml b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Remaps.xml new file mode 100644 index 00000000000..d16b96e5f05 --- /dev/null +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Remaps.xml @@ -0,0 +1,21 @@ + + + + + + From a1ff4c08c467cdf997919114333a5a3196f09c52 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Wed, 18 May 2022 20:27:00 -0400 Subject: [PATCH 09/16] Fix crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When `debug.mono.log` = `gref+`, the app could crash: signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x79c045dead This was likely because a constant string was provided to `OSBridge::_write_stack_trace()`, which tried to write into the constant string, promptly blowing things up. Workaround: don't use `gref+` logging when a GC occurs? Better workaround: Don't Do That™. Don't write to const strings. --- src/monodroid/jni/osbridge.cc | 36 +++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/src/monodroid/jni/osbridge.cc b/src/monodroid/jni/osbridge.cc index c5d147bcac8..d05b0a61947 100644 --- a/src/monodroid/jni/osbridge.cc +++ b/src/monodroid/jni/osbridge.cc @@ -262,7 +262,11 @@ OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject newH threadName, threadId); if (gref_to_logcat) { - _write_stack_trace (nullptr, const_cast(from), LOG_GREF); + if (from_writable) { + _write_stack_trace (nullptr, const_cast(from), LOG_GREF); + } else { + log_info (LOG_GREF, "%s", from); + } } if (!gref_log) return c; @@ -299,7 +303,11 @@ OSBridge::_monodroid_gref_log_delete (jobject handle, char type, const char *thr threadName, threadId); if (gref_to_logcat) { - _write_stack_trace (nullptr, const_cast(from), LOG_GREF); + if (from_writable) { + _write_stack_trace (nullptr, const_cast(from), LOG_GREF); + } else { + log_info (LOG_GREF, "%s", from); + } } if (!gref_log) return; @@ -334,7 +342,11 @@ OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobject new threadName, threadId); if (gref_to_logcat) { - _write_stack_trace (nullptr, const_cast(from), LOG_GREF); + if (from_writable) { + _write_stack_trace (nullptr, const_cast(from), LOG_GREF); + } else { + log_info (LOG_GREF, "%s", from); + } } if (!gref_log) return; @@ -369,7 +381,11 @@ OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const char *th threadName, threadId); if (gref_to_logcat) { - _write_stack_trace (nullptr, const_cast(from), LOG_GREF); + if (from_writable) { + _write_stack_trace (nullptr, const_cast(from), LOG_GREF); + } else { + log_info (LOG_GREF, "%s", from); + } } if (!gref_log) return; @@ -400,7 +416,11 @@ OSBridge::_monodroid_lref_log_new (int lrefc, jobject handle, char type, const c threadName, threadId); if (lref_to_logcat) { - _write_stack_trace (nullptr, const_cast(from), LOG_LREF); + if (from_writable) { + _write_stack_trace (nullptr, const_cast(from), LOG_GREF); + } else { + log_info (LOG_GREF, "%s", from); + } } if (!lref_log) return; @@ -430,7 +450,11 @@ OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type, cons threadName, threadId); if (lref_to_logcat) { - _write_stack_trace (nullptr, const_cast(from), LOG_LREF); + if (from_writable) { + _write_stack_trace (nullptr, const_cast(from), LOG_GREF); + } else { + log_info (LOG_GREF, "%s", from); + } } if (!lref_log) return; From 104b018e36e33131c474247daeb135ec5c225d6b Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Wed, 18 May 2022 20:30:01 -0400 Subject: [PATCH 10/16] Fix remaining unit tests? Move `TypeAndMemberRemapping()` *earlier* in the file. `DotNetInstallAndRun()` appears in the **Tests** tab, but `TypeAndMemberRemapping()` was not, which was very confusing. On a lark, suspect that `TypeAndMemberRemapping()` isn't run because it's after `DotNetDebug()`, which is `[Category("Debugger")]`. Will moving the test fix things? Fix Mono.Android.NET-Tests, via removing a debug message from AndroidRuntime, and test fixes within external/Java.Interop. --- external/Java.Interop | 2 +- .../Android.Runtime/AndroidRuntime.cs | 4 +- .../Tests/XASdkDeployTests.cs | 102 +++++++++--------- .../Runtime-Microsoft.Android.Sdk/Remaps.xml | 8 +- 4 files changed, 61 insertions(+), 55 deletions(-) diff --git a/external/Java.Interop b/external/Java.Interop index bce69180b90..18acd25d0f5 160000 --- a/external/Java.Interop +++ b/external/Java.Interop @@ -1 +1 @@ -Subproject commit bce69180b90f14c71c2eb1a7b5506077057e5bb7 +Subproject commit 18acd25d0f58b477efa06be0848012fa655e57b5 diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index f35002c7278..5b4b754858d 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -310,8 +310,8 @@ protected override IEnumerable GetSimpleReferences (Type type) if (JNIEnv.ReplacementTypes == null) { return null; } - Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: looking for replacement type for `{jniSimpleReference}`"); - Logger.Log (LogLevel.Warn, "*jonp*", new System.Diagnostics.StackTrace (true).ToString ()); + Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: looking for replacement type for `{jniSimpleReference}`; ReplacementTypes? {JNIEnv.ReplacementTypes != null}"); + // Logger.Log (LogLevel.Warn, "*jonp*", new System.Diagnostics.StackTrace (true).ToString ()); if (JNIEnv.ReplacementTypes.TryGetValue (jniSimpleReference, out var v)) { Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: found replacement type: `{jniSimpleReference}` => `{v}`"); return v; diff --git a/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs b/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs index 68c5f1898e3..b0afcfe2663 100644 --- a/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/XASdkDeployTests.cs @@ -84,6 +84,57 @@ public void DotNetInstallAndRun (bool isRelease, bool xamarinForms, bool? publis Assert.IsTrue(didLaunch, "Activity should have started."); } + [Test] + public void TypeAndMemberRemapping ([Values (false, true)] bool isRelease) + { + AssertHasDevices (); + + var proj = new XASdkProject () { + IsRelease = isRelease, + OtherBuildItems = { + new AndroidItem._AndroidRemapMembers ("RemapActivity.xml") { + Encoding = Encoding.UTF8, + TextContent = () => ResourceData.RemapActivityXml, + }, + new AndroidItem.AndroidJavaSource ("RemapActivity.java") { + Encoding = new UTF8Encoding (encoderShouldEmitUTF8Identifier: false), + TextContent = () => ResourceData.RemapActivityJava, + Metadata = { + { "Bind", "True" }, + }, + }, + }, + }; + proj.MainActivity = proj.DefaultMainActivity.Replace (": Activity", ": global::Example.RemapActivity"); + proj.SetRuntimeIdentifier (DeviceAbi); + var relativeProjDir = Path.Combine ("temp", TestName); + var fullProjDir = Path.Combine (Root, relativeProjDir); + TestOutputDirectories [TestContext.CurrentContext.Test.ID] = fullProjDir; + var files = proj.Save (); + proj.Populate (relativeProjDir, files); + proj.CopyNuGetConfig (relativeProjDir); + var dotnet = new DotNetCLI (proj, Path.Combine (fullProjDir, proj.ProjectFilePath)); + + Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed"); + Assert.IsTrue (dotnet.Run (), "`dotnet run` should succeed"); + + bool didLaunch = WaitForActivityToStart (proj.PackageName, "MainActivity", + Path.Combine (fullProjDir, "logcat.log")); + Assert.IsTrue (didLaunch, "MainActivity should have launched!"); + var logcatOutput = File.ReadAllText (Path.Combine (fullProjDir, "logcat.log")); + + StringAssert.Contains ( + "RemapActivity.onMyCreate() invoked!", + logcatOutput, + "Activity.onCreate() wasn't remapped to RemapActivity.onMyCreate()!" + ); + StringAssert.Contains ( + "ViewHelper.mySetOnClickListener() invoked!", + logcatOutput, + "View.setOnClickListener() wasn't remapped to ViewHelper.mySetOnClickListener()!" + ); + } + [Test] [Category ("Debugger"), Category ("Node-4")] public void DotNetDebug () @@ -157,57 +208,6 @@ public void DotNetDebug () WaitFor (2000); Assert.IsTrue (breakpointHit, "Should have a breakpoint"); } - - [Test] - public void TypeAndMemberRemapping ([Values (false, true)] bool isRelease) - { - AssertHasDevices (); - - var proj = new XASdkProject () { - IsRelease = isRelease, - OtherBuildItems = { - new AndroidItem._AndroidRemapMembers ("RemapActivity.xml") { - Encoding = Encoding.UTF8, - TextContent = () => ResourceData.RemapActivityXml, - }, - new AndroidItem.AndroidJavaSource ("RemapActivity.java") { - Encoding = new UTF8Encoding (encoderShouldEmitUTF8Identifier: false), - TextContent = () => ResourceData.RemapActivityJava, - Metadata = { - { "Bind", "True" }, - }, - }, - }, - }; - proj.MainActivity = proj.DefaultMainActivity.Replace (": Activity", ": global::Example.RemapActivity"); - proj.SetRuntimeIdentifier (DeviceAbi); - var relativeProjDir = Path.Combine ("temp", TestName); - var fullProjDir = Path.Combine (Root, relativeProjDir); - TestOutputDirectories [TestContext.CurrentContext.Test.ID] = fullProjDir; - var files = proj.Save (); - proj.Populate (relativeProjDir, files); - proj.CopyNuGetConfig (relativeProjDir); - var dotnet = new DotNetCLI (proj, Path.Combine (fullProjDir, proj.ProjectFilePath)); - - Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed"); - Assert.IsTrue (dotnet.Run (), "`dotnet run` should succeed"); - - bool didLaunch = WaitForActivityToStart (proj.PackageName, "MainActivity", - Path.Combine (fullProjDir, "logcat.log")); - Assert.IsTrue (didLaunch, "MainActivity should have launched!"); - var logcatOutput = File.ReadAllText (Path.Combine (fullProjDir, "logcat.log")); - - StringAssert.Contains ( - "RemapActivity.onMyCreate() invoked!", - logcatOutput, - "Activity.onCreate() wasn't remapped to RemapActivity.onMyCreate()!" - ); - StringAssert.Contains ( - "ViewHelper.mySetOnClickListener() invoked!", - logcatOutput, - "View.setOnClickListener() wasn't remapped to ViewHelper.mySetOnClickListener()!" - ); - } } } #endif diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Remaps.xml b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Remaps.xml index d16b96e5f05..6238cdc346a 100644 --- a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Remaps.xml +++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Remaps.xml @@ -17,5 +17,11 @@ source-type="java/lang/Runtime" source-method-name="remappedToGetRuntime" target-type="java/lang/Runtime" - target-method-name="getRuntime" target-method-instance-to-static="true" /> + target-method-name="getRuntime" target-method-instance-to-static="false" /> + From 29a35684ac6667652baee435c2d4584b305fb962 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Thu, 19 May 2022 03:42:15 -0400 Subject: [PATCH 11/16] Fix CheckIncludedAssemblies() on .NET. --- .../Xamarin.Android.Build.Tests/PackagingTest.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs index fc45875c5ac..308617c3f4e 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs @@ -88,9 +88,21 @@ public void CheckIncludedAssemblies ([Values (false, true)] bool usesAssemblySto "Java.Interop.dll", "Mono.Android.dll", "rc.bin", + "System.Collections.dll", + "System.Collections.Concurrent.dll", + "System.Collections.NonGeneric.dll", + "System.Console.dll", + "System.IO.Compression.dll", + "System.Net.Http.dll", + "System.Net.Primitives.dll", + "System.Net.Requests.dll", "System.Private.CoreLib.dll", "System.Runtime.dll", "System.Linq.dll", + "System.Private.Uri.dll", + "System.Private.Xml.dll", + "System.Security.Cryptography.dll", + "System.Text.RegularExpressions.dll", "UnnamedProject.dll", } : new [] { From d5b057062da4caf0f9bd0d1839c1c0a64237e1ef Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Thu, 19 May 2022 14:08:34 -0400 Subject: [PATCH 12/16] Update *.apkdesc files. --- .../BuildReleaseArm64SimpleDotNet.apkdesc | 60 +++++++++++++++---- .../BuildReleaseArm64XFormsDotNet.apkdesc | 22 +++---- 2 files changed, 59 insertions(+), 23 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc index 7f9de03e661..2f75b4732ca 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc @@ -5,31 +5,67 @@ "Size": 3032 }, "assemblies/Java.Interop.dll": { - "Size": 55100 + "Size": 60751 }, "assemblies/Mono.Android.dll": { - "Size": 88786 + "Size": 153019 }, "assemblies/rc.bin": { "Size": 1131 }, + "assemblies/System.Collections.Concurrent.dll": { + "Size": 8764 + }, + "assemblies/System.Collections.dll": { + "Size": 4201 + }, + "assemblies/System.Collections.NonGeneric.dll": { + "Size": 6239 + }, + "assemblies/System.Console.dll": { + "Size": 6507 + }, + "assemblies/System.IO.Compression.dll": { + "Size": 16164 + }, "assemblies/System.Linq.dll": { - "Size": 9970 + "Size": 10189 + }, + "assemblies/System.Net.Http.dll": { + "Size": 65063 + }, + "assemblies/System.Net.Primitives.dll": { + "Size": 21261 + }, + "assemblies/System.Net.Requests.dll": { + "Size": 3521 }, "assemblies/System.Private.CoreLib.dll": { - "Size": 484888 + "Size": 573152 + }, + "assemblies/System.Private.Uri.dll": { + "Size": 37760 + }, + "assemblies/System.Private.Xml.dll": { + "Size": 140763 }, "assemblies/System.Runtime.dll": { "Size": 2412 }, + "assemblies/System.Security.Cryptography.dll": { + "Size": 7437 + }, + "assemblies/System.Text.RegularExpressions.dll": { + "Size": 9596 + }, "assemblies/UnnamedProject.dll": { - "Size": 3553 + "Size": 3560 }, "classes.dex": { - "Size": 347544 + "Size": 348440 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 483888 + "Size": 484512 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 4667280 @@ -44,19 +80,19 @@ "Size": 146816 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 9272 + "Size": 15904 }, "META-INF/BNDLTOOL.RSA": { "Size": 1213 }, "META-INF/BNDLTOOL.SF": { - "Size": 2469 + "Size": 3773 }, "META-INF/MANIFEST.MF": { - "Size": 2342 + "Size": 3646 }, "res/drawable-hdpi-v4/icon.png": { - "Size": 4791 + "Size": 4762 }, "res/drawable-mdpi-v4/icon.png": { "Size": 2200 @@ -80,5 +116,5 @@ "Size": 1904 } }, - "PackageSize": 2959252 + "PackageSize": 3451764 } \ No newline at end of file diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc index 35e3e7e2061..06cebd1dd52 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc @@ -8,10 +8,10 @@ "Size": 7114 }, "assemblies/Java.Interop.dll": { - "Size": 62071 + "Size": 66821 }, "assemblies/Mono.Android.dll": { - "Size": 445013 + "Size": 448188 }, "assemblies/mscorlib.dll": { "Size": 3892 @@ -41,7 +41,7 @@ "Size": 6106 }, "assemblies/System.Console.dll": { - "Size": 6578 + "Size": 6678 }, "assemblies/System.Core.dll": { "Size": 2057 @@ -86,13 +86,13 @@ "Size": 731431 }, "assemblies/System.Private.DataContractSerialization.dll": { - "Size": 185320 + "Size": 185437 }, "assemblies/System.Private.Uri.dll": { "Size": 42820 }, "assemblies/System.Private.Xml.dll": { - "Size": 216418 + "Size": 221792 }, "assemblies/System.Private.Xml.Linq.dll": { "Size": 16697 @@ -119,7 +119,7 @@ "Size": 1912 }, "assemblies/UnnamedProject.dll": { - "Size": 117244 + "Size": 117250 }, "assemblies/Xamarin.AndroidX.Activity.dll": { "Size": 5941 @@ -188,10 +188,10 @@ "Size": 40004 }, "classes.dex": { - "Size": 3460156 + "Size": 3460820 }, "lib/arm64-v8a/libmonodroid.so": { - "Size": 483888 + "Size": 484512 }, "lib/arm64-v8a/libmonosgen-2.0.so": { "Size": 4667280 @@ -206,7 +206,7 @@ "Size": 146816 }, "lib/arm64-v8a/libxamarin-app.so": { - "Size": 98480 + "Size": 98440 }, "META-INF/android.support.design_material.version": { "Size": 12 @@ -773,7 +773,7 @@ "Size": 470 }, "res/drawable-hdpi-v4/icon.png": { - "Size": 4791 + "Size": 4762 }, "res/drawable-hdpi-v4/notification_bg_low_normal.9.png": { "Size": 212 @@ -1961,5 +1961,5 @@ "Size": 341228 } }, - "PackageSize": 8261965 + "PackageSize": 8278349 } \ No newline at end of file From f685d9da30f55eaf1cb2ed71b5dc774fdc58f708 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Thu, 19 May 2022 14:41:25 -0400 Subject: [PATCH 13/16] "Revert" 4015a41b80c5e4400da6c21ce9f37aa413b9ee7c We've moved it into a unit test. --- .../HelloWorld/HelloWorld.DotNet.csproj | 20 ------------------- samples/HelloWorld/HelloWorld/MainActivity.cs | 7 +------ .../HelloWorld/HelloWorld/RemapActivity.java | 17 ---------------- samples/HelloWorld/HelloWorld/remap.xml | 13 ------------ 4 files changed, 1 insertion(+), 56 deletions(-) delete mode 100644 samples/HelloWorld/HelloWorld/RemapActivity.java delete mode 100644 samples/HelloWorld/HelloWorld/remap.xml diff --git a/samples/HelloWorld/HelloWorld/HelloWorld.DotNet.csproj b/samples/HelloWorld/HelloWorld/HelloWorld.DotNet.csproj index e2d85699922..996e0ddb2da 100644 --- a/samples/HelloWorld/HelloWorld/HelloWorld.DotNet.csproj +++ b/samples/HelloWorld/HelloWorld/HelloWorld.DotNet.csproj @@ -10,24 +10,4 @@ - - <_AndroidRemapMembers Include="remap.xml" /> - - \ No newline at end of file diff --git a/samples/HelloWorld/HelloWorld/MainActivity.cs b/samples/HelloWorld/HelloWorld/MainActivity.cs index c29749e51a6..43d1421ef7f 100644 --- a/samples/HelloWorld/HelloWorld/MainActivity.cs +++ b/samples/HelloWorld/HelloWorld/MainActivity.cs @@ -9,12 +9,7 @@ namespace HelloWorld Label = "HelloWorld", MainLauncher = true, Name = "example.MainActivity")] - public class MainActivity : -#if NET - Example.RemapActivity -#else // NET - Activity -#endif // NET + public class MainActivity : Activity { int count = 1; diff --git a/samples/HelloWorld/HelloWorld/RemapActivity.java b/samples/HelloWorld/HelloWorld/RemapActivity.java deleted file mode 100644 index c1672a609b1..00000000000 --- a/samples/HelloWorld/HelloWorld/RemapActivity.java +++ /dev/null @@ -1,17 +0,0 @@ -package example; - -import android.util.Log; - -public class RemapActivity extends android.app.Activity { - public void onMyCreate (android.os.Bundle bundle) { - Log.d ("*jonp*", "RemapActivity.onMyCreate() invoked!"); - super.onCreate(bundle); - } -} - -class ViewHelper { - public static void mySetOnClickListener (android.view.View view, android.view.View.OnClickListener listener) { - Log.d ("*jonp*", "ViewHelper.mySetOnClickListener() invoked!"); - view.setOnClickListener (listener); - } -} diff --git a/samples/HelloWorld/HelloWorld/remap.xml b/samples/HelloWorld/HelloWorld/remap.xml deleted file mode 100644 index 283a3557045..00000000000 --- a/samples/HelloWorld/HelloWorld/remap.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - From 11bd1dec15037d35fdf392ff0cea83d58ba057b1 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Thu, 19 May 2022 14:41:58 -0400 Subject: [PATCH 14/16] Bump Java.Interop. --- external/Java.Interop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/external/Java.Interop b/external/Java.Interop index 18acd25d0f5..a7cb2229477 160000 --- a/external/Java.Interop +++ b/external/Java.Interop @@ -1 +1 @@ -Subproject commit 18acd25d0f58b477efa06be0848012fa655e57b5 +Subproject commit a7cb22294773afbff39d86d33049e236ece90182 From 6cbc01e662c0336fd34307d730478220f307cdec Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Thu, 19 May 2022 15:24:13 -0400 Subject: [PATCH 15/16] Address comments from @grendello & @dellis1972. --- .../Android.Runtime/AndroidRuntime.cs | 57 ++++++++++++------- src/Mono.Android/Android.Runtime/JNIEnv.cs | 1 - .../Microsoft.Android.Sdk.BuildOrder.targets | 2 + .../Xamarin.Android.Common.targets | 3 +- src/monodroid/jni/monodroid-glue.cc | 2 +- 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs index 5b4b754858d..1530b967188 100644 --- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs +++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs @@ -279,28 +279,40 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl protected override IEnumerable GetSimpleReferences (Type type) { string? j = JNIEnv.TypemapManagedToJava (type); - if (j != null) { - yield return #if NET - GetReplacementTypeCore (j) ?? + j = GetReplacementTypeCore (j) ?? j; #endif // NET - j; - } if (JNIEnv.IsRunningOnDesktop) { - yield return JavaNativeTypeManager.ToJniName (type); + string? d = JavaNativeTypeManager.ToJniName (type); + if (j != null && d != null) { + return new[]{j, d}; + } + if (d != null) { + return new[]{d}; + } } + if (j != null) { + return new[]{j}; + } + return Array.Empty (); } #if NET protected override IReadOnlyList? GetStaticMethodFallbackTypesCore (string jniSimpleReference) { - var slash = jniSimpleReference.LastIndexOf ('/'); - var desugarType = slash <= 0 - ? "Desugar" + jniSimpleReference - : jniSimpleReference.Substring (0, slash+1) + "Desugar" + jniSimpleReference.Substring (slash+1); + ReadOnlySpan name = jniSimpleReference; + int slash = name.LastIndexOf ('/'); + var desugarType = new StringBuilder (jniSimpleReference.Length + "Desugar".Length); + if (slash > 0) { + desugarType.Append (name.Slice (0, slash+1)) + .Append ("Desugar") + .Append (name.Slice (slash+1)); + } else { + desugarType.Append ("Desugar").Append (name); + } return new[]{ - desugarType, + desugarType.ToString (), $"{jniSimpleReference}$-CC" }; } @@ -310,10 +322,7 @@ protected override IEnumerable GetSimpleReferences (Type type) if (JNIEnv.ReplacementTypes == null) { return null; } - Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: looking for replacement type for `{jniSimpleReference}`; ReplacementTypes? {JNIEnv.ReplacementTypes != null}"); - // Logger.Log (LogLevel.Warn, "*jonp*", new System.Diagnostics.StackTrace (true).ToString ()); if (JNIEnv.ReplacementTypes.TryGetValue (jniSimpleReference, out var v)) { - Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: found replacement type: `{jniSimpleReference}` => `{v}`"); return v; } return null; @@ -324,11 +333,10 @@ protected override IEnumerable GetSimpleReferences (Type type) if (JNIEnv.ReplacementMethods == null) { return null; } - Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: looking for replacement method for (\"{jniSourceType}\", \"{jniMethodName}\", \"{jniMethodSignature}\")"); #if !STRUCTURED - if (!JNIEnv.ReplacementMethods.TryGetValue ($"{jniSourceType}\t{jniMethodName}\t{jniMethodSignature}", out var r) && - !JNIEnv.ReplacementMethods.TryGetValue ($"{jniSourceType}\t{jniMethodName}\t{GetMethodSignatureWithoutReturnType ()}", out r) && - !JNIEnv.ReplacementMethods.TryGetValue ($"{jniSourceType}\t{jniMethodName}\t", out r)) { + if (!JNIEnv.ReplacementMethods.TryGetValue (CreateReplacementMethodsKey (jniSourceType, jniMethodName, jniMethodSignature), out var r) && + !JNIEnv.ReplacementMethods.TryGetValue (CreateReplacementMethodsKey (jniSourceType, jniMethodName, GetMethodSignatureWithoutReturnType ()), out r) && + !JNIEnv.ReplacementMethods.TryGetValue (CreateReplacementMethodsKey (jniSourceType, jniMethodName, null), out r)) { return null; } ReadOnlySpan replacementInfo = r; @@ -339,8 +347,6 @@ protected override IEnumerable GetSimpleReferences (Type type) var paramCountStr = GetNextString (ref replacementInfo); var isStaticStr = GetNextString (ref replacementInfo); - Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: targetType={targetType.ToString ()}, targetName={targetName.ToString ()}, targetSig=`{targetSig.ToString()}`, paramCount={paramCountStr.ToString ()}, isStatic={isStaticStr.ToString ()}"); - int? paramCount = null; if (!paramCountStr.IsEmpty) { if (!int.TryParse (paramCountStr, 0, System.Globalization.CultureInfo.InvariantCulture, out var count)) { @@ -374,7 +380,6 @@ protected override IEnumerable GetSimpleReferences (Type type) if (!JNIEnv.ReplacementMethods.TryGetValue ((jniSourceType, jniMethodName, jniMethodSignature), out var r) && !JNIEnv.ReplacementMethods.TryGetValue ((jniSourceType, jniMethodName, GetMethodSignatureWithoutReturnType ()), out r) && !JNIEnv.ReplacementMethods.TryGetValue ((jniSourceType, jniMethodName, null), out r)) { - Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: no method replacement found!"); return null; } var targetSig = r.TargetSignature; @@ -384,7 +389,6 @@ protected override IEnumerable GetSimpleReferences (Type type) paramCount = paramCount ?? JniMemberSignature.GetParameterCountFromMethodSignature (jniMethodSignature); paramCount++; } - Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: found replacement: ({GetValue (r.TargetType)}, {GetValue (r.TargetName)}, {GetValue (r.TargetSignature)}, {r.ParamCount?.ToString () ?? "null"}, {r.TurnStatic})"); return new JniRuntime.ReplacementMethodInfo { SourceJniType = jniSourceType, SourceJniMethodName = jniMethodName, @@ -421,6 +425,15 @@ ReadOnlySpan GetNextString (ref ReadOnlySpan info) return r; } } + + static string CreateReplacementMethodsKey (string? sourceType, string? methodName, string? methodSignature) => + new StringBuilder () + .Append (sourceType) + .Append ('\t') + .Append (methodName) + .Append ('\t') + .Append (methodSignature) + .ToString (); #endif // NET delegate Delegate GetCallbackHandler (); diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index aca983580bf..db8da3d0f37 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -181,7 +181,6 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) #if NET if (args->mappingXml != IntPtr.Zero) { var xml = Encoding.UTF8.GetString ((byte*) args->mappingXml, args->mappingXmlLen); - Logger.Log (LogLevel.Warn, "*jonp*", $"# jonp: mapping xml: len={args->mappingXmlLen}; {xml}"); (ReplacementTypes, ReplacementMethods) = MamXmlParser.ParseStrings (xml); } #endif // NET diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets index 02cf141b5e5..89ba48fabd0 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets @@ -109,6 +109,8 @@ projects, these properties are set in Xamarin.Android.Legacy.targets. _SeparateAppExtensionReferences; $(ResolveReferencesDependsOn); + _ConvertAndroidMamMappingFileToXml; + _CollectAndroidRemapMembers; _AddAndroidCustomMetaData; _ResolveAars; diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index cab6fe75512..97006ecbd88 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -404,8 +404,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. - + 0) { init.mappingXml = env->GetByteArrayElements (mappingXml, nullptr); init.mappingXmlLen = mappingXmlLen; + log_warn (LOG_DEFAULT, "# jonp: mappingXml? len=%i, xml=%p", init.mappingXmlLen, init.mappingXml); } - log_warn (LOG_DEFAULT, "# jonp: mappingXml? len=%i, xml=%p", init.mappingXmlLen, init.mappingXml); #if defined (NET) && defined (ANDROID) MonoError error; From 6436962322825c7a1bfb489337f28465346572f8 Mon Sep 17 00:00:00 2001 From: Jonathan Pryor Date: Thu, 19 May 2022 21:32:37 -0400 Subject: [PATCH 16/16] Bump to xamarin/java.interop/main@1f27ab55 --- .gitmodules | 4 ++-- external/Java.Interop | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 60b2750388b..8b2fa1df307 100644 --- a/.gitmodules +++ b/.gitmodules @@ -12,8 +12,8 @@ branch = main [submodule "external/Java.Interop"] path = external/Java.Interop - url = https://github.com/jonpryor/java.interop.git - branch = jonp-member-remapping + url = https://github.com/xamarin/java.interop.git + branch = main [submodule "external/lz4"] path = external/lz4 url = https://github.com/lz4/lz4.git diff --git a/external/Java.Interop b/external/Java.Interop index a7cb2229477..1f27ab552d0 160000 --- a/external/Java.Interop +++ b/external/Java.Interop @@ -1 +1 @@ -Subproject commit a7cb22294773afbff39d86d33049e236ece90182 +Subproject commit 1f27ab552d03aeb74cdc6f8985fcffbfdb9a7ddf