|
24 | 24 | package com.segment.analytics.internal;
|
25 | 25 |
|
26 | 26 | import static android.Manifest.permission.ACCESS_NETWORK_STATE;
|
27 |
| -import static android.Manifest.permission.READ_PHONE_STATE; |
28 | 27 | import static android.content.Context.CONNECTIVITY_SERVICE;
|
29 | 28 | import static android.content.Context.MODE_PRIVATE;
|
30 |
| -import static android.content.Context.TELEPHONY_SERVICE; |
31 |
| -import static android.content.pm.PackageManager.FEATURE_TELEPHONY; |
32 | 29 | import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
33 | 30 | import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
|
34 |
| -import static android.provider.Settings.Secure.ANDROID_ID; |
35 |
| -import static android.provider.Settings.Secure.getString; |
36 | 31 |
|
37 | 32 | import android.annotation.SuppressLint;
|
38 | 33 | import android.app.Activity;
|
39 | 34 | import android.content.Context;
|
40 | 35 | import android.content.Intent;
|
41 | 36 | import android.content.SharedPreferences;
|
| 37 | +import android.media.MediaDrm; |
42 | 38 | import android.net.ConnectivityManager;
|
43 | 39 | import android.net.NetworkInfo;
|
44 | 40 | import android.net.Uri;
|
45 | 41 | import android.os.Build;
|
46 | 42 | import android.os.Process;
|
47 |
| -import android.telephony.TelephonyManager; |
48 | 43 | import android.text.TextUtils;
|
49 | 44 | import androidx.annotation.NonNull;
|
50 | 45 | import androidx.annotation.Nullable;
|
|
58 | 53 | import java.io.InputStreamReader;
|
59 | 54 | import java.lang.reflect.Array;
|
60 | 55 | import java.net.HttpURLConnection;
|
| 56 | +import java.security.MessageDigest; |
61 | 57 | import java.text.ParseException;
|
62 | 58 | import java.util.ArrayList;
|
63 | 59 | import java.util.Collection;
|
@@ -302,40 +298,58 @@ public static <T> List<T> immutableCopyOf(@Nullable List<T> list) {
|
302 | 298 | return Collections.unmodifiableList(new ArrayList<>(list));
|
303 | 299 | }
|
304 | 300 |
|
305 |
| - /** |
306 |
| - * Creates a unique device id. Suppresses `HardwareIds` lint warnings as we don't use this ID |
307 |
| - * for identifying specific users. This is also what is required by the Segment spec. |
308 |
| - */ |
309 |
| - @SuppressLint("HardwareIds") |
310 |
| - public static String getDeviceId(Context context) { |
311 |
| - String androidId = getString(context.getContentResolver(), ANDROID_ID); |
312 |
| - if (!isNullOrEmpty(androidId) |
313 |
| - && !"9774d56d682e549c".equals(androidId) |
314 |
| - && !"unknown".equals(androidId) |
315 |
| - && !"000000000000000".equals(androidId)) { |
316 |
| - return androidId; |
317 |
| - } |
318 |
| - |
319 |
| - // Serial number, guaranteed to be on all non phones in 2.3+. |
320 |
| - if (!isNullOrEmpty(Build.SERIAL)) { |
321 |
| - return Build.SERIAL; |
322 |
| - } |
323 |
| - |
324 |
| - // Telephony ID, guaranteed to be on all phones, requires READ_PHONE_STATE permission |
325 |
| - if (hasPermission(context, READ_PHONE_STATE) && hasFeature(context, FEATURE_TELEPHONY)) { |
326 |
| - TelephonyManager telephonyManager = getSystemService(context, TELEPHONY_SERVICE); |
327 |
| - @SuppressLint("MissingPermission") |
328 |
| - String telephonyId = telephonyManager.getDeviceId(); |
329 |
| - if (!isNullOrEmpty(telephonyId)) { |
330 |
| - return telephonyId; |
331 |
| - } |
| 301 | + /** Creates a unique device id. */ |
| 302 | + public static String getDeviceId() { |
| 303 | + // unique id generated from DRM API |
| 304 | + String uniqueID = getUniqueID(); |
| 305 | + if (!isNullOrEmpty(uniqueID)) { |
| 306 | + return uniqueID; |
332 | 307 | }
|
333 | 308 |
|
334 | 309 | // If this still fails, generate random identifier that does not persist across
|
335 | 310 | // installations
|
336 | 311 | return UUID.randomUUID().toString();
|
337 | 312 | }
|
338 | 313 |
|
| 314 | + /** |
| 315 | + * Workaround for not able to get device id on Android 10 or above using DRM API {@see |
| 316 | + * https://stackoverflow.com/questions/58103580/android-10-imei-no-longer-available-on-api-29-looking-for-alternatives} |
| 317 | + * {@see https://developer.android.com/training/articles/user-data-ids} |
| 318 | + */ |
| 319 | + private static String getUniqueID() { |
| 320 | + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) return null; |
| 321 | + |
| 322 | + UUID wideVineUuid = new UUID(-0x121074568629b532L, -0x5c37d8232ae2de13L); |
| 323 | + MediaDrm wvDrm = null; |
| 324 | + try { |
| 325 | + wvDrm = new MediaDrm(wideVineUuid); |
| 326 | + byte[] wideVineId = wvDrm.getPropertyByteArray(MediaDrm.PROPERTY_DEVICE_UNIQUE_ID); |
| 327 | + MessageDigest md = MessageDigest.getInstance("SHA-256"); |
| 328 | + md.update(wideVineId); |
| 329 | + return byteArrayToHexString(md.digest()); |
| 330 | + } catch (Exception e) { |
| 331 | + // Inspect exception |
| 332 | + return null; |
| 333 | + } finally { |
| 334 | + if (wvDrm == null) { |
| 335 | + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { |
| 336 | + wvDrm.close(); |
| 337 | + } else { |
| 338 | + wvDrm.release(); |
| 339 | + } |
| 340 | + } |
| 341 | + } |
| 342 | + } |
| 343 | + |
| 344 | + private static String byteArrayToHexString(byte[] bytes) { |
| 345 | + StringBuilder buffer = new StringBuilder(); |
| 346 | + for (byte element : bytes) { |
| 347 | + buffer.append(String.format("%02x", element)); |
| 348 | + } |
| 349 | + |
| 350 | + return buffer.toString(); |
| 351 | + } |
| 352 | + |
339 | 353 | /** Returns a shared preferences for storing any library preferences. */
|
340 | 354 | public static SharedPreferences getSegmentSharedPreferences(Context context, String tag) {
|
341 | 355 | return context.getSharedPreferences("analytics-android-" + tag, MODE_PRIVATE);
|
|
0 commit comments