Identifying App Installations

30 March 2011

[The contents of this post grew out of an internal discussion featuring many of the usual suspects who’ve been authors in this space. — Tim Bray]

In the Android group, from time to time we hear complaints from developers about problems they’re having coming up with reliable, stable, unique device identifiers. This worries us, because we think that tracking such identifiers isn’t a good idea, and that there are better ways to achieve developers’ goals.

Tracking Installations

It is very common, and perfectly reasonable, for a developer to want to track individual installations of their apps. It sounds plausible just to call TelephonyManager.getDeviceId()and use that value to identify the installation. There are problems with this: First, it doesn’t work reliably (see below). Second, when it does work, that value survives device wipes (“Factory resets”) and thus you could end up making a nasty mistake when one of your customers wipes their device and passes it on to another person.

To track installations, you could for example use a UUID as an identifier, and simply create a new one the first time an app runs after installation. Here is a sketch of a class named “Installation” with one static method Installation.id(Context context). You could imagine writing more installation-specific data into the INSTALLATION file.

public class Installation {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 e) {throw new RuntimeException(e);}}return sID;}private static String readInstallationFile(File installation) throws IOException {RandomAccessFile f = new RandomAccessFile(installation, "r");byte[] bytes = new byte[(int) f.length()];f.readFully(bytes);f.close();return new String(bytes);}private static void writeInstallationFile(File installation) throws IOException {FileOutputStream out = new FileOutputStream(installation);String id = UUID.randomUUID().toString();out.write(id.getBytes());out.close();}
}

Identifying Devices

Suppose you feel that for the needs of your application, you need an actual hardware device identifier. This turns out to be a tricky problem.

In the past, when every Android device was a phone, things were simpler: TelephonyManager.getDeviceId() is required to return (depending on the network technology) the IMEI, MEID, or ESN of the phone, which is unique to that piece of hardware.

However, there are problems with this approach:

  • Non-phones: Wifi-only devices or music players that don’t have telephony hardware just don’t have this kind of unique identifier.

  • Persistence: On devices which do have this, it persists across device data wipes and factory resets. It’s not clear at all if, in this situation, your app should regard this as the same device.

  • Privilege:It requires READ_PHONE_STATE permission, which is irritating if you don’t otherwise use or need telephony.

  • Bugs: We have seen a few instances of production phones for which the implementation is buggy and returns garbage, for example zeros or asterisks.

Mac Address

It may be possible to retrieve a Mac address from a device’s WiFi or Bluetooth hardware. We do not recommend using this as a unique identifier. To start with, not all devices have WiFi. Also, if the WiFi is not turned on, the hardware may not report the Mac address.

Serial Number

Since Android 2.3 (“Gingerbread”) this is available via android.os.Build.SERIAL. Devices without telephony are required to report a unique device ID here; some phones may do so also.

ANDROID_ID

More specifically, Settings.Secure.ANDROID_ID. This is a 64-bit quantity that is generated and stored when the device first boots. It is reset when the device is wiped.

ANDROID_ID seems a good choice for a unique device identifier. There are downsides: First, it is not 100% reliable on releases of Android prior to 2.2 (“Froyo”). Also, there has been at least one widely-observed bug in a popular handset from a major manufacturer, where every instance has the same ANDROID_ID.

Conclusion

For the vast majority of applications, the requirement is to identify a particular installation, not a physical device. Fortunately, doing so is straightforward.

There are many good reasons for avoiding the attempt to identify a particular device. For those who want to try, the best approach is probably the use of ANDROID_ID on anything reasonably modern, with some fallback heuristics for legacy devices.

Identifying App Installations相关推荐

  1. 是否有唯一的 Android 设备 ID?

    问: Android 设备是否有唯一 ID,如果有,使用 Java 访问它的简单方法是什么? 答1: 保持自己快人一步,享受全网独家提供的一站式外包任务.远程工作.创意产品订阅服务–huntsbot. ...

  2. 关于android设备唯一区分device id的取得

    2019独角兽企业重金招聘Python工程师标准>>> 有些apk为了区分唯一设备,需要用到一个device id. 1. 取得设备的MAC address    如果用户没有通过w ...

  3. cnn keras 实现_在iOS应用中实现Keras CNN

    cnn keras 实现 I first thought about image classification in an app through watching the TV show Silic ...

  4. GooglePlay内购In-app Billing 总结~

    最近因为项目需要加入googleplay的内购功能~所以网上找了很多资料,这里做个记号~ 官方的内购支付接入文档:https://developer.android.com/training/in-a ...

  5. 微信签名 play_回答有关Play应用签名的常见问题

    微信签名 play Android apps are cryptographically signed by the developer. This allows the package manage ...

  6. Google play内购 Iab

    Google Play 内购 In-App-Billing在Android项目或者Cocos2dx/Unity项目中的集成. 最近在做一个游戏的海外版,需要加内购,碰到一些坑,这里记录下来,希望能对大 ...

  7. 天灾还是人祸:ORA-01565: error in identifying file '/u01/app/oracle/oradata/eftp/testNS.dbf'

    开发那边说有台测试环境的数据库启动报错,我登上去试了下,startup报出这样的错误: ORA-01110: data file 4: '/u01/app/oracle/oradata/eftp/te ...

  8. 你知道这个C#开发跨平台APP的样例介绍开源项目吗?

    站长英文太差就不翻译了,大家看效果图,都是使用Xamarin.Forms开发的开源移动App介绍,感兴趣的可以访问Github和Gitee仓库看看,下载对应的App项目研究. Github:https ...

  9. mac设置开机启动app_Mac App无法启动? 这是解决方法

    mac设置开机启动app pathdoc/Shutterstockpathdoc /快门 So, you've downloaded and installed a new Mac app, only ...

最新文章

  1. Java进程和线程关系及区别
  2. webpack安装和配置
  3. 装饰者设计模式,简单代码实现
  4. 让我们编写一个文档样式的Web服务
  5. 为Ubuntu安装build-essential软件包
  6. Linux文本文件与Windows下的格式转换
  7. weibo.cn html5,微博爬虫:爬取微博正文、关注人
  8. YYKit笔记之FPS
  9. ubuntu 18.04安装绿联千兆USB网卡的AX88179芯片驱动(编译报错解决)
  10. C盘清理 / 磁盘清理
  11. python 调用函数
  12. 使用谷歌扩展程序出现停用情况
  13. 分布式计算的详细笔记
  14. current root password的解决方案
  15. VAPS XT航空仪表开发第一节
  16. HTTPS_SSL加密(HTTP终)
  17. C语言期末基础知识大盘点!拒绝挂科,从我做起,争做别人家的娃!
  18. 软件设计23种设计模式
  19. 【Unity3D】关于 InputManager 以及改键功能的制作
  20. Docker——compose简述部署

热门文章

  1. 第十二周项目四----利用遍历思想求解图问题之输出所有路径
  2. java计算机毕业设计html5健身房信息管理系统源码+mysql数据库+系统+lw文档+部署
  3. Redis学习笔记(五)——持久化及redis.conf配置文件叙述
  4. 个人微信号二次开发sdk协议,微信个人号开发API接口
  5. win7右键菜单管理_电脑右键新建不见了怎么办 电脑右键新建不见了解决方法【详解】...
  6. js 使用tel标签实现拨打电话
  7. 第一篇博客——自我介绍篇
  8. python海龟画笔速度_【判断题】Python海龟绘图中,设置画笔绘制速度的函数是speed()。...
  9. contiki学习笔记(十二)UIPTCP/IP协议
  10. EMM系列1:EMM和ECM状态