Generación de Identificadores Únicos de Dispositivo Android

Fecha de publicación:
Última actualización: 2021-05-30
Autor:

 

Generar u obtener un único identificador para cada dispositivo donde se instala una app puede ser útil para diferentes propósitos. Por ejemplo, en un sistema de votación en línea puede ser interesante evitar múltiples votos en un mismo dispositivo. En los sistemas transaccionales los identificadores únicos sirven para propósitos de trazabilidad. Esto también puede servir para efectos de control de licencias (ej: limitar el número de dispositivos donde se puede instalar la app).

Identificadores únicos de dispositivo Android

 

La información personal del usuario, como su número de documento de identidad o número de teléfono, no debería de ser usada para estos propósitos debido a restricciones de confidencialidad. Tampoco se recomienda el uso de identificadores de hardware, como la dirección MAC o el número IMEI. Una de las razones para evitar el uso de identificadores de hardware en Android es que su obtención requiere permisos elevados. Además, el número IMEI no siempre es único. Algunos de estos números se repiten a pesar de que las normas de GSMA lo prohíben.

No existe un método que arroje un identificador realmente único en todas las versiones de Android. En este artículo combinamos varios de los métodos existentes para cubrir la mayoría de los casos de uso. Podemos comenzar con el método más usado que es el "android_id":

long uid = 0;
String ss;
try{
   ss = Secure.getString(ctx, "android_id");
   if(ss != null){
      uid = new BigInteger(ss, 16).longValue();
      if(uid == 0x9774D56D682E549CL)
         uid = 0; // filtrar valor no único
   }
}catch(Exception ex){
   Log.e("UID", "error", ex);
}
 

Nótese que este código continúa la ejecución en caso de cualquier error inesperado, incluyendo errores numéricos. Si se obtiene el valor hexadecimal 9774D56D682E549C, este valor es descartado, ya que se conoce que el mismo valor puede ser reportado en múltiples dispositivos.

Si el anterior código no nos arroja un valor distinto de cero, entonces podemos intentar obtener la dirección MAC de Bluetooth, suponiendo que la app tiene permisos para usar Bluetooth:

// Desde Android 10 la MAC no puede ser consultada de esta manera
if(uid == 0 && Build.VERSION.SDK_INT <= 28){
   ss = null;
   try{
      BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
      try{
         Field ff = btAdapter.getClass().getDeclaredField("mService");
         ff.setAccessible(true);
         Object oo = ff.get(btAdapter);
         ss = (String)oo.getClass().getDeclaredMethod("getAddress").invoke(oo);
      }catch(Throwable th){
         ss = btAdapter.getAddress();
      }
   }catch(Exception ex){
      Log.e("UID", "error", ex);
   }
   if(ss != null){

      // Convertir MAC a long
      int bb, vv = 0, xx = 0, yy = ss.length();
      boolean pp = false;
      while(xx < yy){
         if((bb = (ss.charAt(xx++) | 0x20) - 0x30) > 9)
            bb -= 0x27;
         if(bb >= 0 && bb <= 0xF){
            if(pp = !pp)
               vv = bb << 4;
            else
               uid = uid << 8 | vv | bb;
         }
      }

      // MAC no válida
      if((int)uid == 0 || uid <= 0 || uid > 0xFFFFFFFFFFFFL)
         uid = 0;
   }
}
 

Si el código anterior tampoco logra obtener un buen valor, entonces una alternativa puede ser tomar la hora exacta con milisegundos, en la cual se instaló un paquete conocido, como "SystemUI". Este valor no es único, pero puede tener una dispersión y persistencia razonablemente buenas:

if(uid == 0){
   try{
      uid = new File(ctx.getPackageManager().getApplicationInfo(
         "com.android.systemui", 0).sourceDir).lastModified();
   }catch(Exception ex){
      Log.e("UID", "error", ex);
   }
}
 

Una combinación de estos 3 métodos permite cubrir casi la totalidad de los casos de uso. Para evitar colisiones entre los 3 o más métodos se recomienda acompañar el identificador único con algún indicador que sirva para diferenciar el método utilizado.

Recuerde que no todos los teléfonos tienen Bluetooth. No todos los dispositivos Android son teléfonos y tienen suscripción móvil e IMEI. No todos los dispositivos Android tienen las mismas apps por defecto disponibles. La diversidad de dispositivos Android genera retos de desarrollo interesantes. Si omitimos compatibilidad con algún dispositivo o grupo de dispositivos podemos perder un segmento del mercado.