android

Cocos2dx (C++, Java & XML) – How to retrieve Android ID & IMEI/Device ID

Posted on Updated on

Problem: I’m currently building an app using Eclipse with Cocos2dx library using C++. A few days ago I was asked whether it is possible to retrieve the IMEI of someones Android device and load it into our app. So I investigated.

Later this thread ( http://discuss.cocos2d-x.org/t/get-device-id-in-cocos2dx-3-3/18960 ) and read that there is (apparently) no function built in this engine to retrieve the device ID for Android and this baffled me for a good day and a half. I also didn’t want to directly modify the cocos2dx library so that was also an issue.

But then I discovered JniHelper library under the JNI folder in my project.
From what I understand, JNI links Cocos2dx with Java for Android. So I investigated some more and found the solution to my problem. Without further ado, let the hacking begin!

First we need to prepare our C++ source file.

Solution:

.h Header file:

std::string getDeviceID();

.cpp implementation file:

// load jni libraries
#include "platform/android/jni/JniHelper.h"
#include <jni.h>

// ... somewhere further down our code

// 
std::string HogeHoge::getDeviceID() {
    std::string str;
    cocos2d::JniMethodInfo methodInfo;

    cocos2d::JniHelper::getStaticMethodInfo( methodInfo, "org/cocos2dx/cpp/AppActivity",  "findDeviceID", "()Ljava/lang/String;" ); // find our Java method
    jstring jpath  = (jstring)methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID);      // For now we use a static method to retrieve a string value using CallStaticObjectMethod
    const char* npath = methodInfo.env->GetStringUTFChars(jpath, NULL);											    
    str = npath;

    // release objects/methods/anything we no longer need
    methodInfo.env->ReleaseStringUTFChars(jpath, npath);
    methodInfo.env->DeleteLocalRef(methodInfo.classID);
    return str.c_str();
}

From the C++ side, you declare where your Java class is located in your project and which class you want to load. In this example this is defined here:

std::string str;
cocos2d::JniMethodInfo methodInfo;
cocos2d::JniHelper::getStaticMethodInfo( methodInfo, "org/cocos2dx/cpp/AppActivity",  "findDeviceID", "()Ljava/lang/String;" );
// etc etc ..

Our method has now been implemented. But we need a way to print out our string value. Since my emulator is broken/slow as a donkey I’ve had to build my app as a .apk and load it straight into my device. Which means no error log will appear. Luckily however we can use Labels from the Cocos2dx Library to print out the value for us:

auto deviceIDLabel = Label::create(this->getDeviceID(), "Arial",100);
deviceIDLabel->setPosition(Point(winSize.width/2, winSize.height/2));
this->addChild(deviceIDLabel);

Our C++ implementation is now ready. Now lets head over to our Java file and write some more code:

.java Java implementation:

package org.cocos2dx.cpp;

import java.util.Locale;

import org.cocos2dx.lib.Cocos2dxActivity;
import com.example.etc.R;
//import android.R;  // <- dont need this
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;


public class AppActivity extends Cocos2dxActivity {
	
	private static String fetchID;
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		fetchID = this.getIMEI();
	}
	
	/**
	 *  From the C++ side, we are calling for a static method. 
         *  Since we cannot use something like "this.etc etc" we need to compromise a bit and declare
         *  a private static string variable, set its value to call the return value of our static string
         *  method upon initialization.
	 * */
	public static String findDeviceID() {
		return fetchID;
	}
	
	/**
	 * Get Android ID
	 * */
	public String getDeviceID() {
		return Secure.getString(this.getContentResolver(), Secure.ANDROID_ID);	
	}
	
	/**
	 * retrieve IMEI
	 * */
	public String getIMEI() {
		TelephonyManager manager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
		return manager.getDeviceId();
	}
}

As stated in my comment:

From the C++ side, we are calling for a static method. Since we cannot use something like “this.etc etc” we need to compromise a bit and declare a private static string variable, set its value to call the return value of our static string method upon initialization.

one last thing – VERY IMPORTANT!!

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

you must add the following in your AndroidManifest.xml or your application will crash.

Now if we run this, our app should show our IMEI/device ID as a simple Label from our Cocos2dx library.

Was this article helpful/did it make sense? Please let me know because I’m terrible at explaining and I’d like to get better at it. Thanks!

Happy coding!

References:
http://developer.android.com/reference/android/telephony/TelephonyManager.html#getDeviceId()
http://qiita.com/hmuronaka/items/9e62acb8ca7425102632
http://discuss.cocos2d-x.org/t/get-device-id-in-cocos2dx-3-3/18960