Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

[Solved] Android Library Plugin and getSimSerialNumber()

Discussion in 'Android' started by Cromfeli, Jul 28, 2015.

  1. Cromfeli

    Cromfeli

    Joined:
    Oct 30, 2014
    Posts:
    202
    Hi all Android developers!

    I have just started exploring and learning how to make Android native plugins for Unity. I got my test plugin up and running yesterday just to get random number from the plugin into Unity project that can be compiled and ran on an Android device. Now I started to up my game and try something more dramatic, that being try to read from the phone what is the SIM card serial number using getSimSerialNumber() but unfortunately on any device I try it, it returns null.

    Can someone more seasoned help me out understanding what is going wrong?

    I first thought I need to create new custom Manifest, just to have permission for READ_PHONE_STATE, but after I made my own Manifest the app does not even launch, it crashes when you try to run. Fortunately enough it seems after investigation that Unity 5.1 adds that permission automatically to its default manifest. And by checking my project stagingarea manifest it is requesting for that permission. So apparently the manifest is not the problem.

    Here is my Android Java project code:
    Code (Java):
    1. package com.Test.myandroidplugin;
    2. import java.util.Random;
    3.  
    4. import android.app.Activity;
    5. import android.content.Context;
    6. import android.telephony.TelephonyManager;
    7.  
    8. public class AndroidPluginAccess extends Activity
    9. {
    10.     public static int ReturnRandomInt()
    11.     {
    12.         int randInt = new Random().nextInt(100);
    13.         return  randInt;
    14.     }
    15.  
    16.     public String ReturnSIMSerialNumber()
    17.     {
    18.         TelephonyManager mTelephonyMgr =
    19.         (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
    20.         String serialString = mTelephonyMgr.getSimSerialNumber();
    21.         return serialString;
    22.     }
    23. }
    And here is my Unity test script:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class GetAndroidPluginData : MonoBehaviour
    5. {
    6.  
    7.     private int randomValueFromAndroid = -1;
    8.     private string serialNumberFromAndroid = "-1";
    9.  
    10.     #if UNITY_ANDROID
    11.     public static int ReturnRandomInt()
    12.     {
    13.         AndroidJavaClass myAndroidJavaClass =
    14.         new AndroidJavaClass("com.Test.myandroidplugin.AndroidPluginAccess");
    15.         return myAndroidJavaClass.CallStatic<int>("ReturnRandomInt");
    16.     }
    17.  
    18.     public string ReturnSIMSerialNumber()
    19.     {
    20.         AndroidJavaClass myAndroidJavaClass =
    21.         new AndroidJavaClass("com.Test.myandroidplugin.AndroidPluginAccess");
    22.         return myAndroidJavaClass.Call<string>("ReturnSIMSerialNumber");
    23.     }
    24.     #endif
    25.  
    26.     public void LogAndroidData ()
    27.     {
    28.         #if UNITY_ANDROID
    29.         randomValueFromAndroid = ReturnRandomInt();
    30.         serialNumberFromAndroid = ReturnSIMSerialNumber();
    31.         #endif
    32.  
    33.         Debug.Log ("Value returned from Android: " + randomValueFromAndroid );
    34.         Debug.Log ("Serial returned from Android: " + serialNumberFromAndroid );
    35.     }
    36. }
    When I deploy to my tablet, the random value is just fine. I call the method from UI and I get new random value. But the SIM card serial number is null on my tablet that has SIM card for 4G and also on Samsung S4 mobile phone that has SIM card.

    Anyone can see if I do something just in a wrong way and that is why I get null?
     
    Last edited: Jul 28, 2015
  2. BitsAtPlayDev

    BitsAtPlayDev

    Joined:
    May 5, 2013
    Posts:
    11
    Hi,

    I think you need to create an instance of the class in order to call non-static methods.

    Try something like:
    Code (csharp):
    1. public string ReturnSIMSerialNumber()
    2. {
    3.   AndroidJavaObject jo =
    4.     new AndroidJavaObject( "com.Test.myandroidplugin.AndroidPluginAccess" );
    5.   return jo.Call<string>("ReturnSIMSerialNumber");
    6. }
     
  3. Cromfeli

    Cromfeli

    Joined:
    Oct 30, 2014
    Posts:
    202
    Thank you so much for help! I tried your approach and got following error:

    AndroidJavaException: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

    I found a possible resolution HERE with:

    Code (Java):
    1. Handler mHandler;
    2.  
    3. public void run(){
    4.   Looper.prepare();
    5.   mHandler = new Handler(){
    6.      public void handleMessage(Message msg){
    7.         Looper.myLooper().quit();
    8.      }
    9.   };
    10.   //do your stuff
    11.   //...
    12.   mHandler.sendEmptyMessage(0); //send ourself a message so the looper can stop itself
    13.   Looper.loop();
    14. }//end of run
    But since I am such a novice, I dont understand what exactly is going wrong and not sure if I need to escalate the problem to the android side code. What I alo tried was to use getSimState, as null for previous is possible and a valid return value. But also with getSimState I always get 0 value as UNKNOWN so I suspect the system appears to be working, but infact is not actually communicating with anything meaningful and just returns these default values instead of giving errors.

    Do you have further proposal? As you can see my initial code at Eclipse side, it is very primitive. On top of that I am just barely starting to learn what all the Android jargon means like Activity, Service etc. So I am immediately worried, probably for nothing!

    EDIT1:

    I also checked logcat to try understand more deeply what went wrong:

    Code (logcat):
    1. 07-29 11:16:51.324: I/InputReader(763): Touch event's action is 0x0 (deviceType=0) [pCnt=1, s=0.28 ] when=1286831053000
    2. 07-29 11:16:51.324: I/InputDispatcher(763): Delivering touch to: action: 0x0, toolType: 1
    3. 07-29 11:16:51.404: I/InputReader(763): Touch event's action is 0x1 (deviceType=0) [pCnt=1, s=] when=1286917171000
    4. 07-29 11:16:51.404: I/InputDispatcher(763): Delivering touch to: action: 0x1, toolType: 1
    5. 07-29 11:16:51.724: I/Unity(7424): AndroidJavaException: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
    6. 07-29 11:16:51.724: I/Unity(7424):   at UnityEngine.AndroidJNISafe.CheckException () [0x00000] in <filename unknown>:0
    7. 07-29 11:16:51.724: I/Unity(7424):   at UnityEngine.AndroidJNISafe.NewObject (IntPtr clazz, IntPtr methodID, UnityEngine.jvalue[] args) [0x00000] in <filename unknown>:0
    8. 07-29 11:16:51.724: I/Unity(7424):   at UnityEngine.AndroidJavaObject._AndroidJavaObject (System.String className, System.Object[] args) [0x00000] in <filename unknown>:0
    9. 07-29 11:16:51.724: I/Unity(7424):   at UnityEngine.AndroidJavaObject..ctor (System.String className, System.Object[] args) [0x00000] in <filename unknown>:0
    10. 07-29 11:16:51.724: I/Unity(7424):   at GetAndroidPluginData.ReturnSIMSerialNumber () [0x00000] in <filename unknown>:0
    11. 07-29 11:16:51.724: I/Unity(7424):   at GetAndroidPluginData.LogAndroidData () [0x00000] in <filename unknown>:0
    12. 07-29 11:16:51.724: I/Unity(7424):   at UnityEngine.Events.InvokableCall.Invoke (System.Object[] args) [0x00000] in <filename unknown>:0
    13. 07-29 11:16:51.724: I/Unity(7424):   at UnityEngine.Events.InvokableCallList.Invoke (System.Object[] parameters) [0x00000] in <filename unknown>:0
    14. 07-29 11:16:51.724: I/Unity(7424):   at UnityEngine.Events.UnityEventBase
    Is it possible that I am missing something on Eclipse side related to Unity? Do I somehow need to account for Unity Activity things there?

    EDIT2:

    My randonInt value gets fixed to 0 if I don't make it static method. And since none of my other methods are static, it seems to cause this problem. Maybe you are right about the instance stuff, but I just am not able to do it in good way.
     
    Last edited: Jul 29, 2015
  4. BitsAtPlayDev

    BitsAtPlayDev

    Joined:
    May 5, 2013
    Posts:
    11
    Try this ...
    Code (Java):
    1. package com.Test.myandroidplugin;
    2.  
    3. import android.app.Activity;
    4. import android.content.Context;
    5. import android.telephony.TelephonyManager;
    6.  
    7. public class AndroidPluginAccess
    8. {
    9.   public String ReturnSIMSerialNumber( Activity curActivity )
    10.   {
    11.     TelephonyManager mTelephonyMgr =
    12.       (TelephonyManager)curActivity.getSystemService( Context.TELEPHONY_SERVICE );
    13.  
    14.     return mTelephonyMgr.getSimSerialNumber();
    15.   }
    16. }

    Code (csharp):
    1. using UnityEngine;
    2.  
    3. public class GetAndroidPluginData : MonoBehaviour
    4. {
    5.   public string ReturnSIMSerialNumber()
    6.   {
    7.     AndroidJavaClass jcUnityPlayer =
    8.       new AndroidJavaClass( "com.unity3d.player.UnityPlayer" );
    9.  
    10.     AndroidJavaObject joUnityActivity =
    11.       jcUnityPlayer.GetStatic<AndroidJavaObject>( "currentActivity" );
    12.  
    13.     AndroidJavaObject joAndroidPluginAccess =
    14.       new AndroidJavaObject( "com.Test.myandroidplugin.AndroidPluginAccess" );
    15.  
    16.     return joAndroidPluginAccess.Call<string>( "ReturnSIMSerialNumber", joUnityActivity );
    17.   }
    18. }

    Notice I get the Activity from Unity and pass it into the Java code.

    The original class was extending Activity but there was no actually Activity associated with it, which is why it caused a
    "AndroidJavaException: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()" when making the call to "getSystemService()".

    HTH
     
    Last edited: Jul 29, 2015
    Daeon777 likes this.
  5. Cromfeli

    Cromfeli

    Joined:
    Oct 30, 2014
    Posts:
    202
    YES!!! That works perfectly! Thank you so much for helping out. Now I can crawl back to my cave and keep studying!

    You are the best!