ing0 ing0 - 4 months ago 10
Android Question

Creating a Globally Unique Android Identifier

When talking about unique Android ID's, I'm sure everyone has seen this, however I too am trying to come up with a solution to uniquely identify any android device. I would be happy to use a publicly released class but I am yet to find one.

In my situation, the requirement is to be able to uniquely identify any devices that have some form of an internet connection (such as GPRS or Wi-Fi) and run on API level 8 (v2.2 Froyo). I'm not aware of any devices that doesn't have Wi-Fi or a SIM so let me know if there is any!

We solved this problem in iOS by using a hash of the Wi-Fi mac address as all iOS devices should have this. However for Android, techniques (such as the answers to the SO post referenced above) refer to the

TelephonyManager
class which can return
null
(such as when the device doesn't have a SIM connection, like with the Nexus 7) or
getContext().getContentResolver(), Secure.ANDROID_ID
which according to here isn't 100% reliable on versions prior to Froyo (which is fortunate for me). It doesn't however state of any issues post-Froyo so again, if there are any, let me know! I've also read this can return
null
. According to here it can change on a factory reset, but that isn't a major concern.

So I put together this to generate a hopefully-unique-GUID:
[This method is not complete!]

public static String GetDeviceId(Context context)
{
// Custom String Hash 1
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("someRandomData"); // Not really needed, but means the stringBuilders value won't ever be null

// TM Device String
final TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
String tmDeviceId = tm.getDeviceId(); // Could well be set to null!
LogMsg.Tmp("TM Device String [" + tmDeviceId + "]");

// Custom String Hash 2
stringBuilder.append(tmDeviceId);
int customHash = stringBuilder.toString().hashCode();
LogMsg.Tmp("Custom String hash [" + customHash + "]");

// Device ID String
String androidIDString = android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
LogMsg.Tmp("Device ID String [" + androidIDString + "]");

// Combined hashes as GUID
UUID deviceUuid = new UUID(androidIDString.hashCode(), ((long)customHash << 32));
LogMsg.Tmp("Combined hashes as GUID [" + deviceUuid.toString() + "]");

return deviceUuid.toString();
}


So you may find
tmDeviceId
being set to
null
, in this case the the
customHash
will be the same regardless of the device but then this combined with
androidIDString
should be globally unique. I think. Obviously I will need to work out if
tmDeviceId
AND
androidIDString
aren't available and throw an exception there.

So... is this overkill? If so, is it safe to just use
context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID
?

Answer

Why don't you get MAC address of the device as you've done in iOS? This can be performed in Android devices as well.

I'll give a code snippet that obtains mac address of the device..

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class HardwareUtil {

    public static String getMacAddress()
    {
        try{
            Process process = Runtime.getRuntime().exec("cat /sys/class/net/eth0/address");
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            int read;
            char[] buffer = new char[4096];
            StringBuffer output = new StringBuffer();
            while ((read = reader.read(buffer)) > 0){
                output.append(buffer, 0, read);
            }
            reader.close();
            process.waitFor();
            String hwaddr = output.toString();
            return hwaddr;
        }catch (IOException e) {
            e.printstacktrace();
        }catch (InterruptedException e){
            e.printstacktrace();
        }
    }

}

HardwareUtil.getMacAddress() will return mac address of the device.

EDIT: If mac address is not appropriate for your situation. Following can be useful!

public static String getDeviceId(Context context) {
    final String deviceId = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
    if (deviceId != null) {
        return deviceId;
    } else {
        return android.os.Build.SERIAL;
    }
}

Don't forget to add following permission to your AndoridManifest.xml file if you use getDeviceId method.

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