AWS SDK Unity 2019

AWS SDK Unity 2019
5th April 2020 23 Comments Tutorial Marcus Lam

If you have read and followed the official AWS SDK webpage, but you can not make it work in Unity 2019. I could help you here.

AWS Console:
https://console.aws.amazon.com/console/

The official AWS Unity SDK:
https://docs.aws.amazon.com/mobile/sdkforunity/developerguide/what-is-unity-plugin.html

First, the AWS SDK should work in Unity 2018. If it does not work, please check the awsconfig.xml and link.xml setup. You may put those files under Assets\Resources. Here is the example for awsconfig.xml and link.xml.

link.xml

<linker>
<!-- if you are using AWSConfigs.HttpClient.UnityWebRequest option-->
<assembly fullname="UnityEngine">
 <type fullname="UnityEngine.Networking.UnityWebRequest" preserve="all" />
 <type fullname="UnityEngine.Networking.UploadHandlerRaw" preserve="all" />
 <type fullname="UnityEngine.Networking.UploadHandler" preserve="all" />
 <type fullname="UnityEngine.Networking.DownloadHandler" preserve="all" />
 <type fullname="UnityEngine.Networking.DownloadHandlerBuffer" preserve="all" />
</assembly>
<assembly fullname="mscorlib">
 <namespace fullname="System.Security.Cryptography" preserve="all"/>
</assembly>
<assembly fullname="System">
 <namespace fullname="System.Security.Cryptography" preserve="all"/>
</assembly>
<assembly fullname="AWSSDK.Core" preserve="all"/>
<assembly fullname="AWSSDK.CognitoIdentity" preserve="all"/>
<assembly fullname="AWSSDK.SecurityToken" preserve="all"/>
<assembly fullname="AWSSDK.DynamoDBv2" preserve="all"/>
</linker>

Download: https://lhkmarcus.com/post_src/link.xml

In this example I am using the DynamoDB service so I add the <assembly fullname=”AWSSDK.DynamoDBv2″ preserve=”all”/> . You should add the service that you are going to use here.

<?xml version="1.0" encoding="utf-8"?> 
<aws correctForClockSkew="true" region="us-east-1"> 
<logging logTo="UnityLogger" logResponses="Always" logMetrics="true" logMetricsFormat="JSON" /> 
<s3 useSignatureVersion4="true" /> </aws>

awsconfig.xml

Download: https://lhkmarcus.com/post_src/awsconfig.xml

You may need to change the “us-east-1” to the region that you are using.

This setting should work in Unity 2018 and Unity 2019 “Editor” and “iOS”, BUT NOT the Android device.

If you have the error about Amazon.AWSConfigs.set_HttpClient, you may add the code below in the “Start” of the program.

AWSConfigs.HttpClient = AWSConfigs.HttpClientOption.UnityWebRequest;

How to fix the AWS SDK for Unity 2019

After you follow the official AWS SDK page steps (including import the official SDK) and setup the awsconfig.xml and link.xml.

  1. Found and open the AndroidManifest.xml file which is under the “Assets\Plugins\Android”.
  2. Change the “com.unity3d.player.UnityPlayerNativeActivity” to “com.unity3d.player.UnityPlayerActivity”
  3. Download the fixed SDK here https://lhkmarcus.com/AWS_SDK_Unity2019.zip
  4. Unzip and replace all the file to the Assets\AWSSDK in your unity project

Now your project should be work in both iOS and Android.

Android IL2CPP

If you want it also work in Android IL2CPP, you would need to add the following code to your class:

#if UNITY_ANDROID
    public void UsedOnlyForAOTCodeGeneration() {
        //Bug reported on github https://github.com/aws/aws-sdk-net/issues/477
        //IL2CPP restrictions: https://docs.unity3d.com/Manual/ScriptingRestrictions.html
        //Inspired workaround: https://docs.unity3d.com/ScriptReference/AndroidJavaObject.Get.html
 
        AndroidJavaObject jo = new AndroidJavaObject("android.os.Message");
        int valueString = jo.Get<int>("what");
    }
#endif

Build your own AWS SDK for Unity

You may want to build your own SDK if you are using other versions of AWS SDK. First you may found the AWS SDK code here: https://github.com/aws/aws-sdk-net. Then you may just change the AndroidInterop.cs that in your project. The AndroidInterop.cs file is under “aws-sdk-net-master\sdk\src\Core\Amazon.Util\Internal_unity”. The file may look like this:

/*
 * Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 * 
 *  http://aws.amazon.com/apache2.0
 * 
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;

namespace Amazon.Util.Internal
{
    /// <summary>
    /// This class is used to make Android Java calls as an alternative to using Android macro's
    /// The class uses reflection but doesnto cache the PropertyInfo and MethodInfo, 
    /// so it should be sparingly used so as not to impact performance.
    /// </summary>
    public class AndroidInterop
    {
        /// <summary>
        /// The API makes a call to a static java method on a class and returns a typed parameter
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="className"></param>
        /// <param name="methodName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static T CallStaticJavaMethod<T>(string className, string methodName, params object[] parameters)
        {
            Type androidJavaClassType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaClass");
            if (androidJavaClassType != null)
            {
                var javaUnityHelper = Activator.CreateInstance(androidJavaClassType, className);
                var callStaticMethod = androidJavaClassType.GetMethods()
                  .Where(x => x.Name == "CallStatic")
                  .FirstOrDefault();
                if (callStaticMethod != null)
                {
                    return (T)callStaticMethod.Invoke(javaUnityHelper, new object[] { methodName, parameters });
                }
            }
            return default(T);
        }

        /// <summary>
        /// The API makes a call to a static java method on a class and returns an object of Type AndroidJavaObject
        /// </summary>
        /// <param name="className"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static object GetJavaObjectStatically(string className, string methodName)
        {
            Type androidJavaClassType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaClass");
            Type androidJavaObjectType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaObject");
            if (androidJavaClassType != null)
            {
                var javaClass = Activator.CreateInstance(androidJavaClassType, className);
                var callStaticMethod = androidJavaClassType.GetMethods()
                    .Where(x => x.Name == "CallStatic")
                    .First(x => x.ContainsGenericParameters);

                var genericStaticMethod = callStaticMethod.MakeGenericMethod(androidJavaObjectType);

                return genericStaticMethod.Invoke(javaClass, new object[] { methodName, new object[] { } });
            }
            return null;
        }

        /// <summary>
        /// This API makes a call to a method on an Object by passing the specified parameters and returns a typed parameter
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="androidJavaObject"></param>
        /// <param name="methodName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static T CallMethod<T>(object androidJavaObject, string methodName, params object[] parameters)
        {
            var method = androidJavaObject.GetType().GetMethods().Where(x => x.Name == "Call").First(x => x.ContainsGenericParameters);
            var genericMethod = method.MakeGenericMethod(typeof(T));
            return (T)genericMethod.Invoke(androidJavaObject, new object[] { methodName, parameters });
        }

        /// <summary>
        /// This API makes a call to a method on an Object by passing the specified parameters and returns an object of type AndroidJavaObject
        /// </summary>
        /// <param name="androidJavaObject"></param>
        /// <param name="methodName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static object CallMethod(object androidJavaObject, string methodName, params object[] parameters)
        {
            Type androidJavaObjectType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaObject");
            var method = androidJavaObject.GetType().GetMethods()
                .Where(x => x.Name == "Call")
                .First(x => x.ContainsGenericParameters);
            var genericMethod = method.MakeGenericMethod(androidJavaObjectType);
            return genericMethod.Invoke(androidJavaObject, new object[] { methodName, parameters });
        }

        /// <summary>
        /// This API get a typed value from a static field 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="className"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static T GetStaticJavaField<T>(string className, string methodName)
        {
            Type androidJavaClassType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaClass");
            if (androidJavaClassType != null)
            {
                var javaUnityHelper = Activator.CreateInstance(androidJavaClassType, className);
                var staticGetter = androidJavaClassType.GetMethod("GetStatic");
                var genericGetter = staticGetter.MakeGenericMethod(typeof(T));

                if (genericGetter != null)
                {
                    return (T)genericGetter.Invoke(javaUnityHelper, new object[] { methodName });
                }
            }

            return default(T);
        }

        /// <summary>
        /// This API returns a value of type AndroidJavaObject from a static field
        /// </summary>
        /// <param name="className"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static object GetStaticJavaField(string className, string methodName)
        {
            Type androidJavaClassType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaClass");
            Type androidJavaObjectType = InternalSDKUtils.GetTypeFromUnityEngine("AndroidJavaObject");
            if (androidJavaClassType != null)
            {
                var javaUnityHelper = Activator.CreateInstance(androidJavaClassType, className);
                var staticGetter = androidJavaClassType.GetMethod("GetStatic");
                var genericGetter = staticGetter.MakeGenericMethod(androidJavaObjectType);

                if (genericGetter != null)
                {
                    return genericGetter.Invoke(javaUnityHelper, new object[] { methodName });
                }
            }
            return null;
        }

        /// <summary>
        /// This API returns a value of type AndroidJavaObject from a field on an android java object
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="androidJavaObject"></param>
        /// <param name="methodName"></param>
        /// <returns></returns>
        public static T GetJavaField<T>(object androidJavaObject, string methodName)
        {
            var method = androidJavaObject.GetType().GetMethods().Where(x => x.Name == "Get").First(x => x.ContainsGenericParameters);
            var genericMethod = method.MakeGenericMethod(typeof(T));
            return (T)genericMethod.Invoke(androidJavaObject, new object[] { methodName });
        }

        /// <summary>
        /// Returns an AndroidJavaObject 
        /// </summary>
        /// <returns></returns>
        public static object GetAndroidContext()
        {
            return GetStaticJavaField("com.unity3d.player.UnityPlayer", "currentActivity");
        }

    }
}

The problem is the androidJavaObject.GetType().GetMethods().Where(). After Unity 2019.1, It requires AndroidJavaObject[] but not the object[]. You may find the fixed AndroidInterop.cs here: https://lhkmarcus.com/post_src/AndroidInterop.cs

AWS SDK Unity 2019

Note

In most of the case, you just replace the AWSSDK.Core.dll would work. If your other dll (ex: AWSSDK.S3.dll, AWSSDK.DynamoDBv2.dll ) is too new/old that not match with the AWSSDK.Core.dll I uploaded, you may need to replace the other dll too.

About The Author
Marcus Lam An Electrical Engineer with programming skill in Hong Kong. Provide technical solutions for hardware or software projects.
Leave Comment
  1. 1

    Peter Mavronicolas

    Harvey the Electrical Engineer from Hong Kong,

    You’re awesome for making the AWS SDK.

    Thank you 😊

    Peter Mavronicolas
    Founder
    Whoa Apps Inc
    [email protected]

    Reply
  2. 1

    Rob Brooks

    Thank you for making available your rebuilt 2019-compatible AWS dlls, Marcus.
    I was having issues with callbacks not firing after calling Async functions and just replacing the AWSSDK.Core.dll with yours appears to have fixed my problems.
    Might it be possible to share your changes so that I can update my own SDK project.
    I’m presuming it’s possibly just a change to AndroidInterop.cs but I’ve been unable to find a fixed copy of this file.
    Many thanks.
    Rob.

    Reply
    1. 1

      Marcus Lam

      Yes, you are right. You may find the changed AndroidInterop.cs file here:
      https://lhkmarcus.com/post_src/AndroidInterop.cs

      I updated the post too.

      Reply
  3. 1

    Marcus Lam

    Yes, you are right. You may find the changed AndroidInterop.cs file here:
    https://lhkmarcus.com/post_src/AndroidInterop.cs

    Reply
  4. 1

    John

    Thanks for doing this Marcus. I tried using those .dll’s with .NET 2 standard will no luck. I also tried .NET 4.x in Unity to no avail. but better progress. I also had to swap the contents you show here between the link and awsconfig xml files.

    I get this error:
    ExecutionEngineException: Attempting to call method ‘UnityEngine.AndroidJavaObject::Get’ for which no ahead of time (AOT) code was generated.
    04-19 13:46:26.426 23861 23906 E Unity : at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in :0
    04-19 13:46:26.426 23861 23906 E Unity : at System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) [0x00000] in :0
    04-19 13:46:26.426 23861 23906 E Unity : at Amazon.Util.Internal.AndroidInterop.GetJavaField[T] (System.Object androidJavaObject, System.String methodName) [0x00000] in :0
    04-19 13:46:26.426 23861 23906 E Unity : at Amazon.Util.Internal.AmazonHookedPlatformInfo.Init () [0x00000] in :0
    04-19 13:46:26.426 23861 23906 E Unity : at Amazon.Util.Internal.AmazonHookedPlatformInfo.get_Instance () [0x00000] in :0
    04-19 13:46:26.426 23861 23906 E Unity : at Amazon.UnityInitializer.Awake () [0x00000] in <000000000000000000000

    Have you seen this before? I'm unable to secure a Congnito IdentityID like before.

    Reply
    1. 1

      Marcus Lam

      Did your app works in unity editor?

      Reply
  5. 1

    stewart

    Hi I am also getting the error:

    05-07 21:55:39.275 30636 30677 E Unity : ExecutionEngineException: Attempting to call method ‘UnityEngine.AndroidJavaObject::Get’ for which no ahead of time (AOT) code was generated.
    05-07 21:55:39.275 30636 30677 E Unity : at System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) [0x00000] in :0
    05-07 21:55:39.275 30636 30677 E Unity : at Amazon.Util.Internal.AndroidInterop.GetJavaField[T] (System.Object androidJavaObject, System.String methodName) [0x00000] in :0
    05-07 21:55:39.275 30636 30677 E Unity : at Amazon.Util.Internal.AmazonHookedPlatformInfo.Init () [0x00000] in :0
    05-07 21:55:39.275 30636 30677 E Unity : at Amazon.Util.Internal.AmazonHookedPlatformInfo.get_Instance () [0x00000] in :0
    05-07 21:55:39.275 30636 30677 E Unity : at Amazon.UnityInitializer.Awake () [0x00000] in :0
    05-07 21:55:39.275 30636 30677 E Unity : at UnityEngine.GameObject.AddComponent[T] () [0x00000] in :0
    05-07 21:55:39.275 30636 30677 E Unity : at Amazon.UnityInitializ
    05-07 21:55:39.278 30636 30677 I Unity : Attached unity initializer to FirehoseProvider

    Works fine in editor but not Android
    This is using Unity 2019.1…

    The only package I add in my project is:
    AWSSDK.Kinesis.3.3.100.173.unitypackage

    Any idea why I may be getting this error?
    Thanks

    Reply
    1. 1

      Marcus Lam

      Did you replace all the dll including AWSSDK.Core.dll?

      Reply
  6. 1

    Stewart

    Hi, yes I replaced just the AWSSDK.Core.dll and tried with also replacing AWSSDK.Core.pdb

    I also tried updating to use the package AWSSDK.Kinesis.3.3.100.173 and then swapping these dlls but the same error occurs

    Reply
    1. 1

      Marcus Lam

      You would need to replace all the dll that in the AWS_SDK_Unity2019.zip.
      It is because if you are using the AWSSDK that newer then the version of my build, it may not work.
      If you are using the AWSSDK.Kinesis, you would need to replace at least:
      AWSSDK.CognitoIdentity.dll
      AWSSDK.Core.dll
      AWSSDK.Kinesis.dll
      AWSSDK.SecurityToken.dll

      Reply
  7. 1

    Stewart

    Hi,

    Yes I actually just tried that, replacing them all with yours (those 4 you listed) and it has the same issue. Works fine in editor but Android has the “Attempting to call method ‘UnityEngine.AndroidJavaObject::Get’ for which no ahead of time (AOT) code was generated.” error.

    Not quite sure why that didn’t work. I may need to make the changes manually myself to see the process.

    Reply
    1. 1

      Marcus Lam

      You may find the AWSSDK code link below this post to build your own SDK.

      Reply
  8. 1

    Devi

    Hello,
    I have followed your steps and your solution works great for Unity + android with mono scripting backend but I need it to be working for IL2CPP.
    Can you please help

    Reply
    1. 1

      Marcus Lam

      Set up a “link.xml” file as described on the Unity SDK readme, which is necessary if you will be building with assembly stripping or IL2CPP. Be sure to add the line

      https://github.com/aws/aws-sdk-net/blob/master/Unity.README.md#unity-sdk-fundamentals

      Reply
    2. 1

      Marcus Lam

      Not sure if you still need AWS to be working for IL2CPP.
      Try to put this code into your class(you do not need to change anything in this code):

      #if UNITY_ANDROID
      public void UsedOnlyForAOTCodeGeneration() {
      AndroidJavaObject jo = new AndroidJavaObject(“android.os.Message”);
      int valueString = jo.Get(“what”);
      }
      #endif

      Reply
  9. 1

    Enosh

    Hello
    Thanks for your precious posting.
    You saved my week
    may god bless you

    Reply
  10. 1

    Joe

    Hi mate,
    Thanks for this post. I am doing an Oculus Quest project research, and I try to use your modified SDK. When I follow your tutorial it works well (even the android version), but when I integrate it to the project it fails at some point.
    So the connexion to the DynamoDB is working well as I am able to create a table, but then when I try to load Data it fails.
    I get 6 errors, I put here only the last one:

    “AmazonDynamoDBException making request PutItemRequest to https://dynamodb.eu-west-2.amazonaws.com/. Attempt 1.
    UnityEngine.Debug:LogError(Object)
    Amazon.Runtime.Internal.Util.UnityDebugLogger:Error(Exception, String, Object[])
    Amazon.Runtime.Internal.Util.Logger:Error(Exception, String, Object[])
    Amazon.Runtime.Internal.RetryHandler:LogForError(IRequestContext, Exception)
    Amazon.Runtime.Internal.RetryHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.PipelineHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.CallbackHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.PipelineHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.EndpointResolver:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.PipelineHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.Marshaller:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.PipelineHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.CallbackHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.PipelineHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.ErrorCallbackHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.PipelineHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.MetricsHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.PipelineHandler:InvokeSync(IExecutionContext)
    Amazon.Runtime.Internal.RuntimePipeline:InvokeSync(IExecutionContext)
    Amazon.Runtime.AmazonServiceClient:Invoke(AmazonWebServiceRequest, InvokeOptionsBase)
    Amazon.DynamoDBv2.AmazonDynamoDBClient:PutItem(PutItemRequest)
    Amazon.DynamoDBv2.DocumentModel.Table:PutItemHelper(Document, PutItemOperationConfig)
    Amazon.DynamoDBv2.DocumentModel.c__DisplayClass89_0:b__0()
    Amazon.DynamoDBv2.c__DisplayClass1_0`1:b__0(Object)
    System.Threading._ThreadPoolWaitCallback:PerformWaitCallback()”

    Reply
    1. 1

      Marcus Lam

      I guess that is related to the link.xml or the awsconfig.xml or the AWS Console setting.
      For the link.xml or the awsconfig.xml file, I am using “us-east-1” in this tutorial. You would need to change all the “us-east-1” to your region “eu-west-2”.
      You would need to change the GameObject inspector too.
      For the AWS Console setting, please watch my video in this post.

      Reply
  11. 1

    Joe

    Yes, I did that. Actually, I think I know what is wrong. There is 2 AndroidManifest.XML in my project, The Quest one and yours.
    Both as MAIN, which is wrong. As AWS should work on the background (the player doesn’t need to see anything from AWS) I have to merge both Manifest and remove the MAIN from your manifest.
    If you have any idea how to do that, that would be great? Thanks

    Ps: Of course I already watched your video (several times lolll), liked it and subscribed 😉

    Reply
    1. 1

      Marcus Lam

      If you do not duplicate the setting in the AndroidManifest.XML(maybe “activity” in this case).
      You may not need to have only one AndroidManifest.XML in your project.
      Unity would combine the AndroidManifest.XML for you.
      You may see this for the detail: https://docs.unity3d.com/Manual/android-manifest.html

      Reply
  12. 1

    John

    I forgot to precise that the oculus manifest overwrites your file in: Assets/Plugins/Android/AndroidManifest.xml, which is the same manifest used by your plugin.
    I need to merge them but have no idea how

    Reply
  13. 1

    osama

    Hi I’m using unity 2020.1.0f1 , with IL2CPP with .Net Standard 2.0 , using latest aws-sdk-unity_3.3.802.0,but its not work for me on build its works on editor I followed all ur steps , I have facebook sdk but which add their dependencies on AndroidManifest.xml Located Asset/Plugin/Android, then I resolved dependencies now added aws dependency there, well I didn’t replace dlls files in which you have given , should i do it , I did it , but it didn’t allow me to build android w/o replace its complete build but sdk doesn’t work ,did you test on latest unity? It revert my project then follow ur steps many times , but no luck , can you guide me , what m i missing ? while as I said I’m using aws-sdk-unity_3.3.802.0 sdk should i replace dlls ? sorry for many words , but I need help

    Reply
  14. 1

    Daniel

    Thank you, your guide literally saved me, I could not find info about it anywhere else.

    Reply

Leave a reply

Your email address will not be published. Required fields are marked *

Translate »