android device id

At some point of time you will need seamless user/device identification for your application.

It is not so simple to generate unique device id on Android because of:

  1. multiple Android versions
  2. Google bugs
  3. root
  4. custom ROM modifications with bad Build.SERIAL or ANDROID_ID
  5. multiuser support (4.2 and up)

First of all you can start with this:

  1. Google blog post “Identifying App Installations” http://android-developers.blogspot.ru/2011/03/identifying-app-installations.html
  2. Read stack overflow discussions http://stackoverflow.com/questions/2785485/is-there-a-unique-android-device-id
  3. Read Samsung documentation http://developer.samsung.com/android/technical-docs/How-to-retrieve-the-Device-Unique-ID-from-android-device

Be aware that ANDROID_ID could be changed after user switch (multiuser in 4.2)

Also there is well known bug for 2.2 devices https://code.google.com/p/android/issues/detail?id=10603 – ANDROID_ID is always 9774d56d682e549c. ANDROID_ID could also consists of spaces, dashes, zeros or asterisks for some Chineese devices.

Sometimes Build.SERIAL could be 0123456789ABCDEF for custom ROM or rooted devices.

If you generate your own id – you can also save it to BackupManager.

We combined all approaches and came up to the solution:

import android.content.Context;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Generates unique device id
 */
public class DeviceIdGenerator {

    private static final String EMULATOR_ANDROID_ID = "9774d56d682e549c";
    private static final String[] BAD_SERIAL_PATTERNS = {"1234567", "abcdef", "dead00beef"};

    public static String readDeviceId(Context context) {
        String deviceId;
        String androidSerialId = null;
        try {
            // try to get device serial number
            androidSerialId = Build.SERIAL;
        } catch (NoSuchFieldError ignored) {
        }

        if (!TextUtils.isEmpty(androidSerialId) && !Build.UNKNOWN.equals(androidSerialId) && !isBadSerial(androidSerialId)) {
            deviceId = androidSerialId;
        } else {
            // try to use Settings.Secure.ANDROID_ID
            // could be different after factory reset
            String androidSecureId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
            if (!TextUtils.isEmpty(androidSecureId) && !EMULATOR_ANDROID_ID.equals(androidSecureId) && !isBadDeviceId(androidSecureId)
                    && androidSecureId.length() == EMULATOR_ANDROID_ID.length()) {
                deviceId = androidSecureId;
            } else {
                deviceId = SoftInstallationId.id(context);
            }
        }
        return UUID.nameUUIDFromBytes(deviceId.getBytes()).toString();
    }

    private static boolean isBadDeviceId(String id) {
        // empty or contains only spaces or 0
        return TextUtils.isEmpty(id) || TextUtils.isEmpty(id.replace('0', ' ').replace('-', ' ').trim());
    }

    private static boolean isBadSerial(String id) {
        if (!TextUtils.isEmpty(id)) {
            id = id.toLowerCase();
            for (String pattern : BAD_SERIAL_PATTERNS) {
                if (id.contains(pattern)) {
                    return true;
                }
            }
        } else {
            return true;
        }
        return false;
    }

    /**
     * Class generates id for current application installation
     */
    private static class SoftInstallationId {
        private static String sID = null;
        private static final String INSTALLATION = "INSTALLATION";

        public synchronized static String id(Context context) {
            if (sID == null) {
                File installation = new File(context.getFilesDir(), INSTALLATION);
                try {
                    if (!installation.exists()) {
                        writeInstallationFile(installation);
                    }
                    sID = readInstallationFile(installation);
                } catch (Exception epicFail) {
                    throw new RuntimeException(epicFail);
                }
            }
            return sID;
        }

        private static String readInstallationFile(File installation) throws IOException {
            RandomAccessFile f = new RandomAccessFile(installation, "r");
            try {
                byte[] bytes = new byte[(int) f.length()];
                f.readFully(bytes);
                f.close();
                return new String(bytes);
            } finally {
                f.close();
            }
        }

        private static void writeInstallationFile(File installation) throws IOException {
            FileOutputStream out = null;
            try {
                out = new FileOutputStream(installation);
                String id = UUID.randomUUID().toString();
                out.write(id.getBytes());
            } finally {
                IOUtils.quietClose(out);
            }
        }
    }
}

This solution may also not cover 100% of devices but I hope it covers at least 99%. If you have any comments/suggestions – welcome.

upd.

based on our experience below the list of manufactures with wrong android_id (Build.MANUFACTURER variable)

“alps”
“ASSISTANT”
“JIAYU”
“JYT”
“MAYSUN”
“MSUN”
“Qumo”
“unknown”
“WST”

You can check and ignore android_id for these devices

users count;uuid;MANUFACTURER;MODEL
59;"05377726-6c06-3ace-833f-217568d99d17";"alps";"Alpha GTR"
16;"05b775f7-1674-3027-8908-1585c318a455";"unknown";"HT-7G"
23;"0809fac3-2fbb-30f8-a2da-eba05719bb38";"MAYSUN";"MID4502"
114;"08e3e2df-b8a5-3a8f-8fbc-6bf1fa598559";"alps";"S4001"
17;"09e8ea9b-e725-3b3c-8cbd-15351fcdec3c";"alps";"B92"
13;"09fffabd-83ce-359b-a536-344c37c9b645";"unknown";"V9-1688-CJ-0308-31S"
17;"0a858215-d482-35be-9297-69d1c2e0a66c";"unknown";"Inch Antares ITWG7003"
16;"0ac08d53-c024-3833-b271-4957804a8d71";"unknown";"AT-706"
13;"0ac08d53-c024-3833-b271-4957804a8d71";"unknown";"SoftwinerEvb"
23;"0f793e3b-c0c8-37b3-be79-74160c5298fd";"unknown";"T-990A"
18;"16b4a309-c52e-3baf-b2b3-0216d101a735";"unknown";"TWD_MID"
3;"1b266eb8-df80-3fe4-9eb7-d0148e907932-0";"unknown";"M7320"
3;"1b266eb8-df80-3fe4-9eb7-d0148e907932-0";"unknown";"P7110"
5;"1b266eb8-df80-3fe4-9eb7-d0148e907932-0";"unknown";"Q29A"
16;"2343517f-ac9b-3a00-a3d8-aae454d63b38";"unknown";"ImPAD 3113"
11;"2449af4a-10cb-33b5-b974-bfe121a989ec";"Qumo";"Qumo Quest500"
19;"2c2a76cd-0f69-3bd0-8664-b890ce248613";"unknown";"ImPAD 3412"
13;"3401f0c5-9fc5-39d8-b019-b443eec35ba0";"unknown";"RP Air S70"
82;"38247013-38d2-3fe1-98da-acc7282264c6";"JYT";"JY-G3"
35;"3a867c8a-6d98-3377-b867-58a06b39f9a1";"MAYSUN";"MID5301"
24;"42aaf303-ffa4-3ac8-89df-9aa2182c49c5";"alps";"ZP300+"
25;"42d45461-8872-3929-a8ba-d68613b80c57";"alps";"JY-G2"
14;"493b295b-2d08-378f-bd5f-8962054dea4b";"MAYSUN";"Alpha GTR"
36;"4cd12d9b-b33c-3159-83ae-064e6b900b60";"WST";"S5301"
14;"4e112401-758c-35ce-8c8a-d1eac803c8c5";"alps";"JY-G2"
14;"4f9f2b3c-87df-35e8-acf9-34a821080fa8";"unknown";"SoftwinerEvb"
17;"51da2d52-45e9-3135-9948-bf20653b4cc6";"unknown";"7133W"
61;"5214285d-b81c-3042-925a-9ea10c7c30b4";"alps";"Alpha GT"
13;"5ddc4aa7-ef45-32d0-9cb1-aeb708c17262";"JYT";"JY-G2"
56;"744cabbe-756e-3626-9cb0-62519e615754";"WST";"S4501M"
40;"85583ede-6663-3f46-8c08-a1ea7df39c52";"MSUN";"Alpha GT"
30;"9109af3a-3fd5-38c1-81b7-affafdde17d6";"alps";"9300+"
37;"932221f6-f3dc-3361-a511-9de02bc3c69e";"MAYSUN";"Alpha GTR"
31;"9371ff0d-ac8a-30ed-8b54-319f85203b8c";"JYT";"G3"
18;"93d3de3f-c14f-3cbd-a75d-a2efb36b89ed-0";"JIAYU";"JY-G3"
38;"9a954350-5208-3e27-abcf-d3869ede96d4";"alps";"Alpha GT"
16;"9c45e53e-0c62-301f-bb88-ac1a474a3172";"unknown";"MoveO!TPC-7VY"
18;"9db5c496-ef7e-3161-90f7-49d52d392570";"JYT";"JY-G3"
17;"a54a0399-05e1-3a3e-bc7a-0501a067ceac";"unknown";"TPC-7VY"
17;"a5a0dabf-8753-3f5d-b3a0-8bb5922ab6c3";"ASSISTANT";"AP-901"
33;"ae4c7efe-6b7e-3eff-a03c-3224ea2ed03a";"WST";"S5301M"
17;"b3d9ca07-e142-31a0-be01-d0a4ff77cfcc";"unknown";"Tab-720"
12;"b8f220dc-a283-3777-bb3b-0c173fa39a1a";"unknown";"A960"
48;"c3977ce9-e172-325c-a935-6f51973401ce";"WST";"S4501"
24;"d14d1d8d-fae0-3cfa-bd83-465f72088106";"alps";"ZP900"
10;"db58b2c1-723b-3021-bde8-d06049a5ea84";"unknown";"MID"
27;"db6e3ac1-78df-30f3-81ed-0b54cc4e35df";"unknown";"Tab-720"
58;"df6c2847-c8f7-3b99-8e7e-e5419bc19446";"MAYSUN";"Alpha GTR"
16;"ee211d46-21ca-3d35-98e1-e543f4ce99ce";"unknown";"J-88"
2;"f10a4e01-d202-309a-a290-b648e11ffde1";"alps";"H5500"
19;"f10a4e01-d202-309a-a290-b648e11ffde1";"alps";"ZP100"
13;"f41de853-dfbf-377e-aa6a-9d67fe393f04";"unknown";"ImPAD 1113"
3;"f924e50b-d8c4-3efa-8618-d3e13cfc40e4";"unknown";"A13 MID"
52;"f924e50b-d8c4-3efa-8618-d3e13cfc40e4";"unknown";"SoftwinerEvb"
12;"f9771db5-5dd0-382f-a590-bc2da5ddb467";"unknown";"MID"
16;"f9771db5-5dd0-382f-a590-bc2da5ddb467";"unknown";"Roverpad 3WT74L"
16;"fb8365f1-6c65-3730-a894-257cdd2d6b5c";"unknown";"ImPAD 2113"
17;"fb8365f1-6c65-3730-a894-257cdd2d6b5c";"unknown";"ImPAD 2412"
12;"fefcd075-e1ca-350b-a8a4-c6b0d3160b0e";"unknown";"A713"

// DL