Get Android deviceId, simple enough…?

Turns out it is not as simple as I thought to get a unique device id to identify an Android device in my app. TelephoneManager.getDeviceId() only works if the device has telephone support (of course), ANDROID_ID is apparently buggy on some pre 2.2 devices (some manufacturers always returning the same value, see http://code.google.com/p/android/issues/detail?id=10603).

This blogpost on the Android Developers blog describes the issues in more detail:

http://android-developers.blogspot.com/2011/03/identifying-app-installations.html

I ended up writing a simple factory class to handle this, falling back to a generated id that is stored in SharedPreferences if all above fails:

public class DeviceIdFactory {

    private static String deviceId;
    private static final String DEVICE_ID = "prefDeviceId";
    /**
     * Get unique device id. 
     * 
     * First try Settings.Secure.ANDROID_ID. Due to a bug some manufacturers will return the same
     * id for Settings.Secure.ANDROID_ID. 
     * 
     * Fallback on TelephonyManager.getDeviceId(), which should return unique device id if telephony is 
     * supported on device.
     * 
     * Last fallback generate unique id for this installation and store as SharedPreference.
     * 
     * @see http://android-developers.blogspot.com/2011/03/identifying-app-installations.html
     * @see http://code.google.com/p/android/issues/detail?id=10603
     * @param context
     * @return unique id for this device (might change on factory resets, device upgrade etc)
     */
    public static String getDeviceId(Context context) {
        if (deviceId == null) {
            synchronized (DeviceIdFactory.class) {
                if (deviceId == null) {
                    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
                    deviceId = prefs.getString(DEVICE_ID, null);
                    if (deviceId == null) {
                        String deviceId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID); 
                        try {
                            if ("9774d56d682e549c".equals(deviceId)) {
                                String tmId = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId();
                                deviceId = tmId == null ? UUID.randomUUID().toString() : UUID.nameUUIDFromBytes(tmId.getBytes("utf8")).toString();
                            } else {
                                deviceId = UUID.nameUUIDFromBytes(deviceId.getBytes("utf8")).toString();
                            }
                        } catch (UnsupportedEncodingException e) {
                            throw new RuntimeException(e);
                        }
                        prefs.edit().putString(DEVICE_ID, deviceId).commit();
                    }
                }
            }
        }
        return deviceId;
    }
}

Note: requires READ_PHONE_STATE permission