How to convert openssl_pkey_get_public and openssl_verify to C - android

Now I'm looking for a way to verify the signature from 'Google inapp billing' system.
I've found 'openssl_pkey_get_public' and 'openssl_verify' functions in php(it is very easy and simple!!), but no example or document for C or C++;;;(I spend last two days for searching it..OTL...)
now I have :
- public key
- signature
- purchase data from google
I want to implement verifying code using C or C++
Is there someone who knows how I can get it?
I've searched belows..
- http://www.nlnetlabs.nl/downloads/publications/hsm/hsm_node21.html
It deals with 'openssl EVP'..but it tells about HSM(hardware security module)
thanks!

below is the answer about what I've asked..
1 means success, 0 is fail..
thanks..
int Verify_GoogleInappBilling_Signature(const char* data, const char* signature, const char* pub_key_id)
{
std::shared_ptr<EVP_MD_CTX> mdctx = std::shared_ptr<EVP_MD_CTX>(EVP_MD_CTX_create(), EVP_MD_CTX_destroy);
const EVP_MD* md = EVP_get_digestbyname("SHA1");
if(NULL == md)
{
return -1;
}
if(0 == EVP_VerifyInit_ex(mdctx.get(), md, NULL))
{
return -1;
}
if(0 == EVP_VerifyUpdate(mdctx.get(), (void*)data, strlen(data)))
{
return -1;
}
std::shared_ptr<BIO> b64 = std::shared_ptr<BIO>(BIO_new(BIO_f_base64()), BIO_free);
BIO_set_flags(b64.get(),BIO_FLAGS_BASE64_NO_NL);
std::shared_ptr<BIO> bPubKey = std::shared_ptr<BIO>(BIO_new(BIO_s_mem()), BIO_free);
BIO_puts(bPubKey.get(),pub_key_id);
BIO_push(b64.get(), bPubKey.get());
std::shared_ptr<EVP_PKEY> pubkey = std::shared_ptr<EVP_PKEY>(d2i_PUBKEY_bio(b64.get(), NULL), EVP_PKEY_free);
std::string decoded_signature = Base64Decode(std::string(signature));
return EVP_VerifyFinal(mdctx.get(), (unsigned char*)decoded_signature.c_str(), decoded_signature.length(), pubkey.get());
}

Related

Cannot play a video from internal storage with the native codec given by Google using adb

I am using the native codec app given by Google: (https://github.com/googlesamples/android-ndk/tree/master/native-codec).
The app has a folder (assets) which contains some video samples to play.
My purpose is to read videos from the internal storage of the phone (i.e /sdcard/filename.mp4).
I added these 2 lines to the manifest file but this hasn't helped to fix the issue yet.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
I modified the code to get the video filename as an argument given by adb shell.
Here is the code:
mSourceString = getIntent().getStringExtra("arg");
if (!mCreated) {
if (mSourceString != null) {
mCreated = createStreamingMediaPlayer(getResources().getAssets(), mSourceString);
}
}
if (mCreated) {
mIsPlaying = !mIsPlaying;
setPlayingStreamingMediaPlayer(mIsPlaying);
}
The native code of the method which reads the video filename is the following:
jboolean Java_com_example_mohammed_myapplication_MainActivity_createStreamingMediaPlayer(JNIEnv* env, jclass clazz, jobject assetMgr, jstring filename)
{
LOGV("### create");
// convert Java string to UTF-8
const char *utf8 = env->GetStringUTFChars(filename, NULL);
LOGV("opening %s", utf8);
off_t outStart, outLen;
int fd = AAsset_openFileDescriptor(AAssetManager_open(AAssetManager_fromJava(env, assetMgr), utf8, 0),
&outStart, &outLen);
env->ReleaseStringUTFChars(filename, utf8);
if (fd < 0) {
LOGE("failed to open file: %s %d (%s)", utf8, fd, strerror(errno));
return JNI_FALSE;
}
data.fd = fd;
workerdata *d = &data;
AMediaExtractor *ex = AMediaExtractor_new();
media_status_t err = AMediaExtractor_setDataSourceFd(ex, d->fd,
static_cast<off64_t>(outStart),
static_cast<off64_t>(outLen));
close(d->fd);
if (err != AMEDIA_OK) {
LOGV("setDataSource error: %d", err);
return JNI_FALSE;
}
int numtracks = AMediaExtractor_getTrackCount(ex);
AMediaCodec *codec = NULL;
LOGV("input has %d tracks", numtracks);
for (int i = 0; i < numtracks; i++) {
AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
const char *s = AMediaFormat_toString(format);
LOGV("track %d format: %s", i, s);
const char *mime;
if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
LOGV("no mime type");
return JNI_FALSE;
} else if (!strncmp(mime, "video/", 6)) {
// Omitting most error handling for clarity.
// Production code should check for errors.
AMediaExtractor_selectTrack(ex, i);
codec = AMediaCodec_createDecoderByType(mime);
AMediaCodec_configure(codec, format, d->window, NULL, 0);
d->ex = ex;
d->codec = codec;
d->renderstart = -1;
d->sawInputEOS = false;
d->sawOutputEOS = false;
d->isPlaying = false;
d->renderonce = true;
AMediaCodec_start(codec);
}
AMediaFormat_delete(format);
}
mlooper = new mylooper();
mlooper->post(kMsgCodecBuffer, d);
return JNI_TRUE;
}
The app plays the videos successfully when they are in the "assets" folder, i.e inside the app. But when a video is outside the app (internal/external storage) the app stops working.
Is there a solution for this issue?
Apart from adding storage permission, the user needs to give manual permission also.
For testing purpose, you can go to Settings-> Apps-> your app-> Permissions-> enable storage permission. Should work fine then.
For production purpose, you should ask for permission via dialogue. There are plenty of tutorials for that.

Replacing WCHAR and wchar_t with something generic

I'm trying to convert some VS 2015 C++ code to Android Studio C++ code.
My function looks like this:
int size = 0;
int len = 0;
fread(&size,sizeof(int),1,g_File);
#ifdef VERBOSE
printf("fullSentences size = %d\n",size);
#endif
int i1 = 0;
int i2 = 0;
int i3 = 0;
for(int i = 0; i < size; i++)
{
fread(&len,sizeof(int),1,g_File);
wchar_t *buff = new WCHAR[len+1];
fread(buff,sizeof(WCHAR),len,g_File);
buff[len]=0;
fread(&i1,sizeof(int),1,g_File);
fread(&i2,sizeof(int),1,g_File);
fread(&i3,sizeof(int),1,g_File);
fullSentences.Add(buff,i1,i2,i3);
delete buff;
#ifdef VERBOSE
FullSentence fs = fullSentences.Content().back();
printf("%s\t%d\t%d\n",fs.Text.c_str(),fs.ByteStart,fs.ByteCount);
#endif
}
I would like to get rid of the WCHAR and wchar_t in order to make the porting easier.
Can anybody suggestion a replacement for these?
I would like to avoid having to tell Android Studio what wchar_t is, if possible.
Thank you.
Edit:
Here is the class information:
//
// -------------------- clsFullSentences -----------------------------
//
vector<FullSentence> &clsFullSentences::Content()
{
return m_content;
}
vector<wstring> &clsFullSentences::CleanLower()
{
return m_sCleanLower;
}
void clsFullSentences::LoadSerializedFullSentences(string uFile)
{
if (!fileExists(stringToWString(uFile)))
{
DebugBreak();
}
FILE* inFile = fopen(uFile.c_str(), "rb");
wchar_t signature[2];
fread(signature, sizeof(wchar_t), 1, inFile);
wstring wline;
//read how many possibleresults we have
getLineW(inFile, wline);
unsigned int count=_wtoi(wline.c_str());
for (unsigned int i = 0; i < count; i++)
{
FullSentence st;
getLineW(inFile,wline);
st.Text = wline;
//getLineW(inFile,wline);
st.Emotion =0;// _wtoi(wline.c_str());
getLineW(inFile,wline);
st.ByteStart = _wtoi(wline.c_str());
getLineW(inFile,wline);
st.ByteCount = _wtoi(wline.c_str());
m_content.push_back(st);
}
fclose(inFile);
}
void clsFullSentences::Add(wstring text, int i1, int i2, int i3)
{
FullSentence fs;
fs.Text = text;
fs.Emotion = i1;
fs.ByteStart = i2;
fs.ByteCount = i3;
m_content.push_back(fs);
wstring sClean;
sClean=StripPuncToLower(text);
m_sCleanLower.push_back(sClean);
}
bool getLineW(FILE *inFile, wstring &result)
{
wchar_t data[2] = { 0, 0 };
result = L"";
do
{
fread(data, sizeof(wchar_t), 1, inFile);
if (data[0] > 0)
{
if (data[0] != 13)
{
if (data[0] != 10)
{
result += data;
}
else
{
break;//10 is the end of the line
}
}
}
} while (!feof(inFile));
if (result.size() > 0)
{
return true;
}
else
{
return false;
}
}
If you want this code to work on Windows and Android builds then you will find that on windows wchar_t is 2 bytes, while on android it is 4bytes. So you will not be able to write a file on windows, and later read it properly on android. In such case you would have to use char16_t on android and convert it to 'your
choosen' string type under android.
(edit: even better, if you can - make sure all the files are written as utf8 strings)
As for the 'choosen string type' I would suggest to use utf8, so instead of std::wstring use std::string (encoded using utf8). From my experience NDK team was discouraging use of wchar_t from the very begining (missing w* functions from c library etc.) I am not sure how it is now.
I work on a project which was originally coded with MFC, and then ported to android. We used from the very begining a TCHAR macro. Which as you know resolves to char on non unicode builds and to wchar_t on unicode builds. So the idea is to use TCHAR everywhere then under android TCHAR should resolve to char, while on windows use wchar_t (I assume you use unicode build under windows?).
I am not saying this is a best solution to share code between windows and android platform, there are lots of problems like conversions to utf8 on android, these are done with if-defs.
Use std::string that contains text in UTF-8 encoding. You may also want some lightweight library that helps to deal with checks and conversions of such text. Avoid using wchar_t and std::wstring in code that is meant to be portable.

android get usb_interface_descriptor, usb_endpoint_descriptor

In the method usb_device_new() they are reading the usb_device_descriptor with this function call:
read(fd, device->desc, sizeof(device->desc));
First of all how does that even work, to read from the file descriptor after you have opened the device. How do you know that the usb_device_descriptor is what will be read? I find that somewhat confusing.
And the second question that I have is how can I read the usb_interface_descriptor and the usb_endpoint_descriptor from the usb_device?
Thanks.
I have found how they manage to retrive the interface and endpoints details:
while ((desc = usb_descriptor_iter_next(&iter)) != NULL)
{
if (desc->bDescriptorType == USB_DT_INTERFACE)
{
struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *) desc;
interfaceValues.add(interface->bInterfaceNumber);
interfaceValues.add(interface->bInterfaceClass);
interfaceValues.add(interface->bInterfaceSubClass);
interfaceValues.add(interface->bInterfaceProtocol);
interfaceValues.add(interface->bNumEndpoints);
}
else if (desc->bDescriptorType == USB_DT_ENDPOINT)
{
struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *) desc;
endpointValues.add(endpoint->bEndpointAddress);
endpointValues.add(endpoint->bmAttributes);
endpointValues.add(__le16_to_cpu(endpoint->wMaxPacketSize));
endpointValues.add(endpoint->bInterval);
}
}
I just had to dig a little bit deeper in the framework, in this file.

Receive complete android unicode input in C/C++

(Android, NDK, C++, OpenGL ES)
I need a way to reliably receive the text input from a (soft)keyboard.
The solution can be through Java using a NativeActivity subclass, or anything which works.
At the end I need whatever text is being typed, so I can render it myself with OpenGL
Some background:
Up until now I was triggering the soft keyboard by calling showSoftInput or hideSoftInputFromWindow thought JNI. This never failed so far.
However, the problem is the native activity will not send all characters. Especially some unicode characters outside of ASCII range, or some motion soft keyboard won't work (AKeyEvent_getKeyCode)
It used to be possible to get some of those other unicode characters why checking for KeyEvent.ACTION_MULTIPLE and reading a string of characters.
But even this won't work reliably anymore.
So far I failed to find an alternative method.
I experimented with programmatically adding a EditText, but never got it to work. Even trying to add a simple Button resulted in the OpenGL view to no longer being rendered.
On iOS I worked around it by having a hiding edit box, which I simply activated to make the keyboard show up. I would then read out the edit box and use the string to render myself in OpenGL.
I have the same issues, and I have solved it using a 'Character' event that I process separately from the InputEvent.
The problem is this: AKeyEvent_getKeyCode doesn't return the KeyCode for some softkey events, notably the expanded 'unicode/latin' characters when you hold down a key. This prevents the methods #Shammi and #eozgonul from working because the KeyEvent reconstructed on the Java side doesn't have enough information to get a unicode character.
Another issue is that the InputQueue is drained on the C++/Native side before the dispatchKeyEvent event(s) are fired. This means that the KEYDOWN/KEYUP events all fired before the Java code can process the events. (They are not interleaved).
My solution is to capture the unicode characters on the Java side by overriding dispatchKeyEvent and sending the characters to a Queue<Integer> queueLastInputCharacter = new ConcurrentLinkedQueue<Integer>();
// [JAVA]
#Override
public boolean dispatchKeyEvent (KeyEvent event)
{
int metaState = event.getMetaState();
int unichar = event.getUnicodeChar(metaState);
// We are queuing the Unicode version of the characters for
// sending to the app during processEvents() call.
// We Queue the KeyDown and ActionMultiple Event UnicodeCharacters
if(event.getAction()==KeyEvent.ACTION_DOWN){
if(unichar != 0){
queueLastInputCharacter.offer(Integer.valueOf(unichar));
}
else{
unichar = event.getUnicodeChar();
if(unichar != 0){
queueLastInputCharacter.offer(Integer.valueOf(unichar));
}
else if (event.getDisplayLabel() != 0){
String aText = new String();
aText = "";
aText += event.getDisplayLabel();
queueLastInputCharacter.offer(Integer.valueOf(Character.codePointAt(aText, 0)));
}
else
queueLastInputCharacter.offer(Integer.valueOf(0));
}
}
else if(event.getAction()==KeyEvent.ACTION_MULTIPLE){
unichar = (Character.codePointAt(event.getCharacters(), 0));
queueLastInputCharacter.offer(Integer.valueOf(unichar));
}
return super.dispatchKeyEvent(event);
}
The concurrent queue is going to let the threads play nice together.
I have a Java side method that returns the last input character:
// [JAVA]
public int getLastUnicodeChar(){
if(!queueLastInputCharacter.isEmpty())
return queueLastInputCharacter.poll().intValue();
return 0;
}
At the end of my looper code, I tacked on an extra check to see if the queue retained any unicode characters:
// [C++]
int ident;
int events;
struct android_poll_source* source;
// If not rendering, we will block 250ms waiting for events.
// If animating, we loop until all events are read, then continue
// to draw the next frame of animation.
while ((ident = ALooper_pollAll(((nv_app_status_focused(_lpApp)) ? 1 : 250),
NULL,
&events,
(void**)&source)) >= 0)
{
// Process this event.
if (source != NULL)
source->process(_lpApp, source);
// Check if we are exiting. If so, dump out
if (!nv_app_status_running(_lpApp))
return;
}
static int modtime = 10; // let's not run on every call
if(--modtime == 0) {
long uniChar = androidUnicodeCharFromKeyEvent();
while (uniChar != 0) {
KEvent kCharEvent; // Game engine event
kCharEvent.ptkKey = K_VK_ERROR;
kCharEvent.unicodeChar = uniChar;
kCharEvent.character = uniChar;
/* Send unicode char */
kCharEvent.type = K_EVENT_UNICHAR;
_lpPortableHandler(&kCharEvent);
if (kCharEvent.character < 127) {
/* Send ascii char for source compatibility as well */
kCharEvent.type = K_EVENT_CHAR;
_lpPortableHandler(&kCharEvent);
}
uniChar = androidUnicodeCharFromKeyEvent();
}
modtime = 10;
}
The androidUnicodeCharFromKeyEvent function is very similar to #Shammi 's GetStringFromAInputEvent method, only use CallIntMethod to return the jint.
Notes
This does require modifying your engine to process character events separate from Key events. Android still has key codes like AKEYCODE_BACK or AKEYCODE_ENTER that are not character events and still need to be handled (and can be handled on the main input looper).
Editboxes, consoles, etc... Things that are expecting user input can be modified to receive a separate character event that builds the string. If you are working on multiple platforms, then you will need to generate these new character events in addition to the normal key input events.
I hope this works for you, worked for me so far.
int GetUnicodeChar(struct android_app* app, int eventType, int keyCode, int metaState)
{
JavaVM* javaVM = app->activity->vm;
JNIEnv* jniEnv = app->activity->env;
JavaVMAttachArgs attachArgs;
attachArgs.version = JNI_VERSION_1_6;
attachArgs.name = "NativeThread";
attachArgs.group = NULL;
jint result = javaVM->AttachCurrentThread(&jniEnv, &attachArgs);
if(result == JNI_ERR)
{
return 0;
}
jclass class_key_event = jniEnv->FindClass("android/view/KeyEvent");
int unicodeKey;
if(metaState == 0)
{
jmethodID method_get_unicode_char = jniEnv->GetMethodID(class_key_event, "getUnicodeChar", "()I");
jmethodID eventConstructor = jniEnv->GetMethodID(class_key_event, "<init>", "(II)V");
jobject eventObj = jniEnv->NewObject(class_key_event, eventConstructor, eventType, keyCode);
unicodeKey = jniEnv->CallIntMethod(eventObj, method_get_unicode_char);
}
else
{
jmethodID method_get_unicode_char = jniEnv->GetMethodID(class_key_event, "getUnicodeChar", "(I)I");
jmethodID eventConstructor = jniEnv->GetMethodID(class_key_event, "<init>", "(II)V");
jobject eventObj = jniEnv->NewObject(class_key_event, eventConstructor, eventType, keyCode);
unicodeKey = jniEnv->CallIntMethod(eventObj, method_get_unicode_char, metaState);
}
javaVM->DetachCurrentThread();
LOGI("Unicode key is: %d", unicodeKey);
return unicodeKey;
}
Just call it from your input handler, my structure is approximately as follows:
switch (AInputEvent_getType(event))
{
case AINPUT_EVENT_TYPE_KEY:
switch (AKeyEvent_getAction(event))
{
case AKEY_EVENT_ACTION_DOWN:
int key = AKeyEvent_getKeyCode(event);
int metaState = AKeyEvent_getMetaState(event);
int uniValue;
if(metaState != 0)
uniValue = GetUnicodeChar(app, AKEY_EVENT_ACTION_DOWN, key, metaState);
else
uniValue = GetUnicodeChar(app, AKEY_EVENT_ACTION_DOWN, key, 0);
Since you stated that you already open the soft keyboard, I don't go into that part but the code is kind of straight forward. I basically use the Java function of class KeyEvent which has GetUnicodeChar function.
Eozgonul's solution worked for me. I adopted it and modified it to split the work between Java and the native side. Basically I extend NativeActivity to derive my own class which allows me to move as much as possible to Java. I also ended up passing all the data from the input event. I wanted to make sure I captured as much as possible in the created KeyEvent object.
package com.MyCompany.MyApp;
import android.os.Bundle;
import android.view.inputmethod.InputMethodManager;
import android.content.Context;
import android.view.KeyEvent;
public class MyNativeActivity extends android.app.NativeActivity
{
// Need this for screen rotation to send configuration changed callbacks to native
#Override
public void onConfigurationChanged( android.content.res.Configuration newConfig )
{
super.onConfigurationChanged( newConfig );
}
public void showKeyboard()
{
InputMethodManager imm = ( InputMethodManager )getSystemService( Context.INPUT_METHOD_SERVICE );
imm.showSoftInput( this.getWindow().getDecorView(), InputMethodManager.SHOW_FORCED );
}
public void hideKeyboard()
{
InputMethodManager imm = ( InputMethodManager )getSystemService( Context.INPUT_METHOD_SERVICE );
imm.hideSoftInputFromWindow( this.getWindow().getDecorView().getWindowToken(), 0 );
}
public String stringFromKeyCode( long downTime, long eventTime,
int eventAction, int keyCode, int repeatCount, int metaState,
int deviceId, int scanCode, int flags, int source )
{
String strReturn;
KeyEvent keyEvent = new KeyEvent( downTime, eventTime, eventAction, keyCode, repeatCount, metaState, deviceId, scanCode, flags, source );
if ( metaState == 0 )
{
int unicodeChar = keyEvent.getUnicodeChar();
if ( eventAction == KeyEvent.ACTION_MULTIPLE && unicodeChar == keyEvent.KEYCODE_UNKNOWN )
{
strReturn = keyEvent.getCharacters();
}
else
{
strReturn = Character.toString( ( char )unicodeChar );
}
}
else
{
strReturn = Character.toString( ( char )( keyEvent.getUnicodeChar( metaState ) ) );
}
return strReturn;
}
}
On the native side...
std::string GetStringFromAInputEvent( android_app* pApp, AInputEvent* pInputEvent )
{
std::string strReturn;
JavaVM* pJavaVM = pApp->activity->vm;
JNIEnv* pJNIEnv = pApp->activity->env;
JavaVMAttachArgs javaVMAttachArgs;
javaVMAttachArgs.version = JNI_VERSION_1_6;
javaVMAttachArgs.name = "NativeThread";
javaVMAttachArgs.group = NULL;
jint jResult;
jResult = pJavaVM->AttachCurrentThread( &pJNIEnv, &javaVMAttachArgs );
if ( jResult != JNI_ERR )
{
// Retrieves NativeActivity.
jobject nativeActivity = pNativeActivity->clazz;
jclass ClassNativeActivity = pJNIEnv->GetObjectClass( nativeActivity );
jmethodID MethodStringFromKeyCode = pJNIEnv->GetMethodID( ClassNativeActivity, "stringFromKeyCode", "(JJIIIIIIII)Ljava/lang/String;" );
jlong jDownTime = AKeyEvent_getDownTime( pInputEvent );
jlong jEventTime = AKeyEvent_getEventTime( pInputEvent );
jint jEventAction = AKeyEvent_getAction( pInputEvent );
jint jKeyCode = AKeyEvent_getKeyCode( pInputEvent );
jint jRepeatCount = AKeyEvent_getRepeatCount( pInputEvent );
jint jMetaState = AKeyEvent_getMetaState( pInputEvent );
jint jDeviceID = AInputEvent_getDeviceId( pInputEvent );
jint jScanCode = AKeyEvent_getScanCode( pInputEvent );
jint jFlags = AKeyEvent_getFlags( pInputEvent );
jint jSource = AInputEvent_getSource( pInputEvent );
jstring jKeyCodeString = ( jstring )pJNIEnv->CallObjectMethod( nativeActivity, MethodStringFromKeyCode,
jDownTime, jEventTime, jEventAction,
jKeyCode, jRepeatCount, jMetaState,
jDeviceID, jScanCode, jFlags, jSource );
const char* keyCodeString = pJNIEnv->GetStringUTFChars( keyCodeString, nullptr );
strReturn = std::string( keyCodeString );
pJNIEnv->ReleaseStringUTFChars( jKeyCodeString, keyCodeString );
// Finished with the JVM.
pJavaVM->DetachCurrentThread();
}
return strReturn;
}
The 2 reasons I went with this approach..
Reduces code syntax complexity by moving code to java and only having you to call one jni wrapper method on the native side.
Java is the preferred Android language and this allows me to quickly iterate on java based solutions. Moreover most existing solutions are in java.
Basically this will solve the issue.
NativeActivity override onKeyDown()
But you'll have to implement some other way than the NDK key input to get the onKeyMultiple's event.getCharacters() string into your code.

Find mobile device identifier (UDID or Android id)

I want to find out the list of mobile devices which are all click my ad. Based on the details I want to re-target them
So I need to capture their identifier(UDID in ios and Android id in android phone) in order to identify them.
Can anyone suggest me good method or prefer any better way to achieve the above.
For Android, you should check this thread :
Will TelephonyManger.getDeviceId() return device id for Tablets like Galaxy Tab...?
Jorgesys' answer seems clean.
ANDROID:
I am using this and seems to be working fine.
public static String getDeviceAndroidID(Context context)
{
String android_id = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
if(android_id != null)
return android_id;
else
return "";
}
iOS:
For iOS version less than 7 I am using MAC address of the device. From iOS version 7 apple is providing a unique ID for this purpose.
(NSString *)getMacAddress
{
if(SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"7.0"))
{
NSString *strUID = nil;
if(strUID == nil) strUID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
return strUID;
}
int mgmtInfoBase[6];
char *msgBuffer = NULL;
size_t length;
unsigned char macAddress[6];
struct if_msghdr *interfaceMsgStruct;
struct sockaddr_dl *socketStruct;
NSString *errorFlag = nil;
// Setup the management Information Base (mib)
mgmtInfoBase[0] = CTL_NET; // Request network subsystem
mgmtInfoBase[1] = AF_ROUTE; // Routing table info
mgmtInfoBase[2] = 0;
mgmtInfoBase[3] = AF_LINK; // Request link layer information
mgmtInfoBase[4] = NET_RT_IFLIST; // Request all configured interfaces
// With all configured interfaces requested, get handle index
if ((mgmtInfoBase[5] = if_nametoindex("en0")) == 0)
errorFlag = #"if_nametoindex failure";
else
{
// Get the size of the data available (store in len)
if (sysctl(mgmtInfoBase, 6, NULL, &length, NULL, 0) < 0)
errorFlag = #"sysctl mgmtInfoBase failure";
else
{
// Alloc memory based on above call
if ((msgBuffer = malloc(length)) == NULL)
errorFlag = #"buffer allocation failure";
else
{
// Get system information, store in buffer
if (sysctl(mgmtInfoBase, 6, msgBuffer, &length, NULL, 0) < 0)
errorFlag = #"sysctl msgBuffer failure";
}
}
}
// Befor going any further...
if (errorFlag != nil)
{
free(msgBuffer);
if(ENABLE_LOG) DLog(#"Error: %#", errorFlag);
return errorFlag;
}
// Map msgbuffer to interface message structure
interfaceMsgStruct = (struct if_msghdr *) msgBuffer;
// Map to link-level socket structure
socketStruct = (struct sockaddr_dl *) (interfaceMsgStruct + 1);
// Copy link layer address data in socket structure to an array
memcpy(&macAddress, socketStruct->sdl_data + socketStruct->sdl_nlen, 6);
// Read from char array into a string object, into traditional Mac address format
NSString *macAddressString = [NSString stringWithFormat:#"%02X:%02X:%02X:%02X:%02X:%02X",
macAddress[0], macAddress[1], macAddress[2],
macAddress[3], macAddress[4], macAddress[5]];
//if(ENABLE_LOG) DLog(#"Mac Address: %#", macAddressString);
// Release the buffer memory
free(msgBuffer);
return macAddressString;
}

Categories

Resources