6

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?

Community
  • 1
  • 1
ingh.am
  • 24,319
  • 41
  • 126
  • 176
  • 1
    "In my situation, the requirement is to be able to uniquely identify any devices" -- if you need this to cover rooted device users, this is impossible AFAIK. Certainly, any of the pieces that you are using can be modified by somebody with root privileges. "I'm not aware of any devices that doesn't have Wi-Fi or a SIM so let me know if there is any!" -- Google TV devices may not have WiFi, going with Ethernet. If you are distributing beyond the Play Store, there may be other devices without either mobile data or WiFi. – CommonsWare Feb 14 '13 at 17:07
  • @CommonsWare thanks for your response. My initial thoughts is to just not support the device if the methods I talked about in the question all return `null`. Do you think it's safe to use `Secure.ANDROID_ID` on anything from Froyo? Also, I don't think a rooted device is a major concern. I guess it would allow someone to spoof a different device of someone else, at least, if they knew what they were doing, but I was thinking that by adding a key (where I had `stringBuilder.append("someRandomData");`) would make this difficult to generate the final returned device ID. – ingh.am Feb 15 '13 at 09:36
  • " Do you think it's safe to use Secure.ANDROID_ID on anything from Froyo?" -- rooted device users can change it, and devices that do not legitimately have the Play Store on it might not have unique values. – CommonsWare Feb 15 '13 at 12:32
  • Ah right OK. I think I'll use the Android ID and combine it with a hash of a key custom to the application. I know it could result in a duplicate GUID but I'm mainly just looking to stopping two devices using our software from having the same ID. I guess the other option is to generate my own GUID and store it, if the app gets uninstalled then goodbye to that GUID. I'm using SQLCipher so at least that's safer. I guess a rooted device can access my sqlitedb too. – ingh.am Feb 15 '13 at 13:31
  • "I'm using SQLCipher so at least that's safer" -- assuming you're trying your own DRM scheme, with a password known to you and not the user, this is not especially difficult to break. SQLCipher is to help the user defend against miscreants, not to help developers defend against users. – CommonsWare Feb 15 '13 at 13:43
  • I'm not using SQLCipher specifically for this, but I was under the impression it would be difficult to modify without the access key. – ingh.am Feb 15 '13 at 13:49
  • And getting the access key (a.k.a., passphrase) out of your app is not especially difficult. – CommonsWare Feb 15 '13 at 13:56
  • Fair point, so there's no real way to fully protect against this then. I'm assuming you would do this by means of obfuscation? – ingh.am Feb 15 '13 at 13:59
  • RE: Security, I've just been reading http://stackoverflow.com/questions/9865162, which contains a good answer with links to interesting articles. – ingh.am Feb 15 '13 at 14:21
  • 1
    "I'm assuming you would do this by means of obfuscation?" -- data cannot be obfuscated using ProGuard. DexGuard, a commercial tool from the makers of ProGuard, apparently can do this. However, since somebody can simply mod the APK to intercept the passphrase, this will be of limited use. – CommonsWare Feb 15 '13 at 17:03
  • @CommonsWare You should put some of your comments in as answers. Not the first legacy post I've visited that already has an answer from you :) – Warpzit Jul 12 '13 at 14:08
  • 1
    @Warpzit: I did not post an answer, because this comment thread doesn't really answer the question, other than to point out issues with the question itself. Sometimes, if I chime in with a comment, and it turns out that it is indeed the answer, I will convert it into an answer. – CommonsWare Jul 12 '13 at 14:13
  • @CommonsWare You helped answering that context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID isn't safe but I guess the other part was kinda left out since you went for the problems with the question :) – Warpzit Jul 12 '13 at 14:19
  • Have you tried Google Cloud Messaging api `String regId= GoogleCloudMessaging.getInstance(context) = gcm.register(SENDER_ID);` It is not an UUID but it's unique to your application. – Raúl Juárez Aug 25 '13 at 19:09

1 Answers1

3

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" />
gokhanakkurt
  • 4,845
  • 4
  • 27
  • 39
  • This is what I've done in the end, the problem is I believe you can spoof the MAC address if you have root access. – ingh.am Oct 07 '13 at 16:10
  • Yeah you are absolutely right. If you have root device, you can do that. Have u tried android.os.Build.SERIAL? You can get hardware serial number by using this? Check this out [link](http://developer.android.com/reference/android/os/Build.html#SERIAL) It may be helpful for you. – gokhanakkurt Oct 08 '13 at 07:06
  • Thanks, I'll check that out. Is `android.os.Build.SERIAL` something that is guaranteed to exist? – ingh.am Oct 08 '13 at 10:51
  • Yep, it is available since API level 9. I will edit my answer and give another code snippet. – gokhanakkurt Oct 08 '13 at 11:54
  • I'm not sure about that. Only thing that I know devices are required to report that number. However, API document says that "A hardware serial number, if available." So we can simply derive an equation "this may not be available all devices". By the way, what do you mean by saying all devices? Tablets? Smart TVs? Microwaves :) – gokhanakkurt Oct 08 '13 at 16:06
  • Hey, for my own purposes I can rely on the mac address due to the nature of the application I'm building, but I was just curious for any future readers. Through experimentation and discussions on SO, I'm pretty sure that you can never guarantee anything in android! I guess the key is to stick with Google Play store for distribution and have your permissions filter out those that you cannot support. – ingh.am Oct 08 '13 at 23:36
  • Btw, I've given you the answer as that is what I ended up doing anyway. – ingh.am Oct 08 '13 at 23:37