安卓获取APP对应的Android id的原理分析

2023-05-16

android_id 的生成原理是由系统生成的随机数,并与应用 app 签名,经过 HmacSHA256 算法生成的;
从 android 8 以后开始就是随机的了,每个应用获取到的简要步骤;
获取的方式如下所示:

androidId = android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
 public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
            final boolean isSelf = (userHandle == UserHandle.myUserId());
            if (isSelf) {
                synchronized (NameValueCache.this) {
                    if (mGenerationTracker != null) {
                        if (mGenerationTracker.isGenerationChanged()) {
                            if (true) {
                                Log.i(TAG, "Generation changed for type:"
                                        + mUri.getPath() + " in package:"
                                        + cr.getPackageName() +" and user:" + userHandle);
                            }
                            mValues.clear();
                        } else if (mValues.containsKey(name)) {
                            //如果APP已经安装第二次读取位置
                            Log.v(TAG, "getStringForUser clear name= " + name  + " key: " + mValues.get(name));
                            mValues.remove(name);
                           // return mValues.get(name);
                        }
                    }
                }
             final long token = Binder.clearCallingIdentity();
                        try {
							Log.w(TAG, "call here ------" );
                            b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
                        } finally {
                            Binder.restoreCallingIdentity(token);
                        }
                    } else {
                        b = cp.call(cr.getPackageName(), mCallGetCommand, name, args);
                    }
                    if (b != null) {
						//这里 call getSecureSetting获取 value
                        String value = b.getString(Settings.NameValueTable.VALUE);
						Log.w(TAG, "NameValueCache b.getString(Settings " + value + " isSelf " + isSelf + " needsGenerationTracker " + needsGenerationTracker);
						Log.w(TAG, "NameValueCache value " + value);
                        // Don't update our cache for reads of other users' data
                        if (isSelf) {
                            synchronized (NameValueCache.this) {
                                if (needsGenerationTracker) {
                                    MemoryIntArray array = b.getParcelable(
                                            CALL_METHOD_TRACK_GENERATION_KEY);
                                    final int index = b.getInt(
                                            CALL_METHOD_GENERATION_INDEX_KEY, -1);
                                    if (array != null && index >= 0) {
                                        final int generation = b.getInt(
                                                CALL_METHOD_GENERATION_KEY, 0);
                                        if (true) {
                                            Log.i(TAG, "Received generation tracker for type:"
                                                    + mUri.getPath() + " in package:"
                                                    + cr.getPackageName() + " and user:"
                                                    + userHandle + " with index:" + index);
                                        }
                                        if (mGenerationTracker != null) {
                                            mGenerationTracker.destroy();
                                        }
                                        mGenerationTracker = new GenerationTracker(array, index,
                                                generation, () -> {
                                            synchronized (NameValueCache.this) {
                                                Log.e(TAG, "Error accessing generation"
                                                        + " tracker - removing");
                                                if (mGenerationTracker != null) {
                                                    GenerationTracker generationTracker =
                                                            mGenerationTracker;
                                                    mGenerationTracker = null;
                                                    generationTracker.destroy();
                                                    mValues.clear();
                                                }
                                            }
                                        });
                                    }
                                }
								//第一次APP安装执行到这里,保存生成的Android id
                                mValues.put(name, value);
                            }

首先从缓存mValues变量中去找,如果没有查询到,就调用SettingsProvider的call()接口,如果call()接口也没有查询到,再调用query()接口。这里用的是call()接口,下文就以call()接口往下分析。cp.call()会调用到SettingsProvider的call()方法
APP安装后第一次尝试读取Android id ,mValues 一定为NULL,所有就会调用SettingsProvider 相关接口读取Android id。读取成功会保存到mValues缓存中。
frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
接着会调用到SettingsProvider.java内部函数getSecureSetting

private Setting getSecureSetting(String name, int requestingUserId) {
	执行到这里说明此App在缓存中不存在。在这里获取 
	if (isNewSsaidSetting(name)) {
            PackageInfo callingPkg = getCallingPackageInfo(owningUserId);
            synchronized (mLock) {
                //getStringForUser -> getSecureSetting ,进入下一步生成android id
                Slog.e(LOG_TAG, "getSecureSetting to getSsaidSettingLocked " +  callingPkg.packageName + " owningUserId " + 	owningUserId);
                return getSsaidSettingLocked(callingPkg, owningUserId);
            }
        }
}
 private Setting getSsaidSettingLocked(PackageInfo callingPkg, int owningUserId) {
 //首先获取APP的 userid,因为Android id 是根据 key = userid 保存在文件和settings中
	 String name = Integer.toString(
                UserHandle.getUid(owningUserId, UserHandle.getAppId(Binder.getCallingUid())));
                //获取msettings 保存的 app 的 android id 对应的 setting
                final Setting ssaid = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID, owningUserId,
                name);
                //获取 setting对应的SettingsState
                final SettingsState ssaidSettings = mSettingsRegistry.getSettingsLocked(
                SETTINGS_TYPE_SSAID, owningUserId);
		//如果 app 对应的setting 不存在,就重新生成 走到这里生成userkey
		if (ssaid == null || ssaid.isNull() || ssaid.getValue() == null) {
			 Slog.e(LOG_TAG, "ssaid is null . go generate====");
            Setting setting = mSettingsRegistry.generateSsaidLocked(callingPkg, owningUserId);
            return mascaradeSsaidSetting(ssaidSettings, setting);
        }
	return mascaradeSsaidSetting(ssaidSettings, ssaid);
 }

上段代码主要是获取相关的app 对应的setting ,获取不到就重新生成一个。
下面就进入到真正生成Android id的相关逻辑流程

        public Setting generateSsaidLocked(PackageInfo callingPkg, int userId) {
            // Read the user's key from the ssaid table.
            //这里获取生成Android id所用的系统随机数
            Setting userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY);
            if (userKeySetting == null || userKeySetting.isNull()
                    || userKeySetting.getValue() == null) {
                // Lazy initialize and store the user key.
                //如果默认的系统key不存在,这里生成默认Android userkey
                Slog.e(LOG_TAG, "generateSsaidLocked -------");
                generateUserKeyLocked(userId);
                userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY);
                if (userKeySetting == null || userKeySetting.isNull()
                        || userKeySetting.getValue() == null) {
                    throw new IllegalStateException("User key not accessible");
                }
            }
            final String userKey = userKeySetting.getValue();
          

            // Convert the user's key back to a byte array.
            final byte[] keyBytes = ByteStringUtils.fromHexToByteArray(userKey);

            // Validate that the key is of expected length.
            // Keys are currently 32 bytes, but were once 16 bytes during Android O development.
            if (keyBytes == null || (keyBytes.length != 16 && keyBytes.length != 32)) {
                throw new IllegalStateException("User key invalid");
            }
			//下面就是对APP做相关的算法生成Android id
            final Mac m;
            try {
                m = Mac.getInstance("HmacSHA256");
                m.init(new SecretKeySpec(keyBytes, m.getAlgorithm()));
            } catch (NoSuchAlgorithmException e) {
                throw new IllegalStateException("HmacSHA256 is not available", e);
            } catch (InvalidKeyException e) {
                throw new IllegalStateException("Key is corrupted", e);
            }
			Slog.e(LOG_TAG, "generateSsaidLocked -------Mac " +  m);

            // Mac each of the developer signatures.
            for (int i = 0; i < callingPkg.signatures.length; i++) {
                byte[] sig = callingPkg.signatures[i].toByteArray();
                m.update(getLengthPrefix(sig), 0, 4);
                m.update(sig);
            }

            // Convert result to a string for storage in settings table. Only want first 64 bits.
            final String ssaid = ByteStringUtils.toHexString(m.doFinal()).substring(0, 16)
                    .toLowerCase(Locale.US);
		

            // Save the ssaid in the ssaid table.
            final String uid = Integer.toString(callingPkg.applicationInfo.uid);
			生成新的Android id 插入到msetting表中和写入到ssid.xml中
            final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId);
            final boolean success = ssaidSettings.insertSettingLocked(uid, ssaid, null, true,
                callingPkg.packageName);

            if (!success) {
                throw new IllegalStateException("Ssaid settings not accessible");
            }

            return getSettingLocked(SETTINGS_TYPE_SSAID, userId, uid);
        }

Android生成 userkey 所用的生成随机数的代码如下所示。

	final byte[] keyBytes1 = new byte[32];
	final SecureRandom rand1 = new SecureRandom();
	rand1.nextBytes(keyBytes1);
	final String userKey = ByteStringUtils.toHexString(keyBytes1);

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

安卓获取APP对应的Android id的原理分析 的相关文章

  • btrfs snapshot快照与回滚

    拍快照是 btrfs subvolume snapshot source dir dest dir snapshot name 回滚方式 xff1a 将原目录更名mv source dir source dir back 利用快照目录再次快
  • 简单方便的linux定时任务管理器——supervisor

    背景 我们在linux服务器上部署模型或者一个长期运行的程序的时候 xff0c 通常使用的是nohup方法 xff0c 因为nohup真的非常简单 xff0c 只需要一行命令即可 xff0c 但是nohup不好的地方就在于无法监控 xff0
  • HTTP、HTTPS等常用的默认端口号

    最近部署一个接口访问对方域名 xff0c https开头Url 端口配成80 xff0c 犯了个低级错误 重新学习一下 端口号标识了一个主机上进行通信的不同的应用程序 1 xff0c HTTP服务器 xff0c 默认端口号为80 tcp x
  • Msg3.0.db可以删吗?一招教你释放C盘50个G空间!

    相信很多朋友经常遇到C盘爆满的时候 xff0c 网上很多方法解决的并不彻底 xff0c 而且很多时候不具有通用性 今天给大家介绍一下如何使用WinDirStat来快速释放自己的硬盘 xff01 WinDirStat 这款神器自己可以自行百度
  • dpkg介绍

    dpkg dpkg a medium level package manager for Debian With dpkg set selections you can set which packages are to be instal
  • 【常用算法】辗转相除法求最大公约数

    辗转相除法 xff0c 又称欧几里德算法 xff08 Euclidean Algorithm xff09 xff0c 是求两个数的最大公约数 xff08 greatest common divisor xff09 的一种方法 用较大的数除以
  • Python人工智能之图片识别,Python3一行代码实现图片文字识别

    自学Python3第5天 xff0c 今天突发奇想 xff0c 想用Python识别图片里的文字 没想到Python实现图片文字识别这么简单 xff0c 只需要一行代码就能搞定 作者微信 xff1a 2501902696 from PIL
  • 动态模型

    1 动态模型 1 1 说明 动态模型是指模型的属性是不固定的 xff0c 可以添加和变更 xff1b 也指不同模型 xff0c 可以增加模型 不同模型在存储上为了性能和隔离 xff0c 使用不同的表 但逻辑编写为了复用 xff0c 一般使用
  • axios请求数据的格式(create)

    post请求 请求的路径 http localhost 9528 dev api admin acl user save 数据格式 xff1a form对象 xff0c 数据在请求体里面 user js文件中 export const re
  • Linux查看mysql使用的是哪个my.cnf

    1 查看是否使用了指定目录的my cnf ps aux grep mysql grep 39 my cnf 39 2 查看mysql默认读取my cnf的目录 mysql help grep 39 my cnf 39 会按顺序加载 3 启动
  • 使用C#跨PC 远程调用程序并显示UI界面

    在项目中有一个需求是需要在局域网内跨PC远程调用一个程序 xff0c 并且要求有界面显示 xff0c 调查了一些资料 xff0c 能实现远程调用的 Net技术大概有PsExec WMI Schedule Task 这三种方式都做了一个尝试
  • Android 获取设备唯一号 unknown

    Android 获取设备唯一号 unknown 问题 xff1a 使用 Build SERIAL 获取设备唯一号时有些机型会返回 39 unknown 39 原因 xff1a 在Android O以后 android os Build SE
  • C++ string类如何format(格式化)字符串

    string类的使用 很遗憾 xff0c string类并没有提供关于format 格式化 字符串的操作 xff0c 但是我们可以借助其他方法来实现 方法一 xff08 推荐 xff09 xff1a ostringstream类实现 ost
  • Codeforces加速访问及其他编程比赛平台推荐

    最近在学习算法 xff0c 刷题 学的差不多准备去比赛试一试 这里分享一些平时比赛平台 文章的开始先给大家推荐一个公众号 小辅导 互联网开发者 多年一线程序开发经验 不定期分享各类资源 https mp weixin qq com s Gq
  • IOS开发UIScrollView控件详解

    首先实现UIScrollViewDelegate协议 xff1a plain view plain copy import lt UIKit UIKit h gt 64 interface Activity01ViewController
  • 【测试开发】几种常见的自动化测试框架

    几种常见的自动化测试框架 在软件测试领域 xff0c 自动化测试框架有很多 xff0c 这里主要介绍几种常用的自动化测试框架 1 pytest pytest 是 Python 的一种单元测试框架 xff0c 与 Python 自带的 uni
  • MPV 快捷键

    按键功能RIGHT前进 5 秒LEFT后退 5 秒UP前进 60 秒DOWN后退 60 秒 0 9091 倍速播放 1 1 倍速播放 0 5 倍速播放 2 0 倍速播放Backspace还原到 1 0 倍速Space 或 p播放 暂停 下一
  • 清除DNS 缓存记录

    original link http www linuxfly org post 543 为了提高DNS 解析的记录 xff0c 很多操作系统都会提供缓存DNS 记录的功能 xff0c 但是 xff0c 这可能会为测试DNS 服务或域名设定
  • Jetson TX2更换软件源

    TX2的软件源为国外服务器 xff0c 网速会很慢 xff0c 需要换国内的ARM源 备份 etc lib路径下的source list文件 xff0c 然后在终端 xff08 按ctrl 43 alt 43 T打开 xff09 执行以下命
  • python和Microsoft Visual C++ Build Tools版本安装

    在windows环境下 xff0c python需要调用Microsoft Visual C 43 43 compiler编译器 xff0c 尤其是在安装第三方包时候 xff0c 会build项目 xff0c 这时如果没有安装或者安装不协调

随机推荐

  • WebSocket 实现数据实时刷新

    WebSocket 是HTML5的一个新协议 xff0c WebSocket 使得客户端和服务器之间的数据交换变得更加简单 xff0c 允许服务端主动向客户端推送数据 在 WebSocket API 中 xff0c 浏览器和服务器只需要完成
  • 计算机毕业设计论文资料查找

    学位论文的材料准备 所谓材料 xff0c 就是为科学研究和论文写作的需要而搜集的一系列事实和事理 撰写学位论文 xff0c 首先要占有丰富的材料 xff0c 这是科研和写作的基础 在这基础上 xff0c 对材料进行加工整理 综合分析 xff
  • 使用VS Code编写、调试和运行C++程序。

    上次提到的是VS Code怎么编写 调试和运行pytho程序 xff0c 这次来说说使用VS Code来对C 43 43 程序操作是怎么个流程和注意哪些问题 环境说明 xff1a Ubuntu18 04 VS Code g 43 43 编译
  • 搭建gitlab

    安装参照 https blog csdn net duyusean article details 80011540 配置的阿里云邮箱 Email Settings gitlab rails 39 gitlab email enabled
  • webrtc系列-kurento相关的一些尝试

    公司最近要做个ICU探视系统 于是在网上找了一个开源的webrtc实现kurentokurento github地址 https github com Kurento kurento media server 下载kurento docke
  • P1080 国王游戏(c++)

    题目 xff1a 题意 xff1a 找出获得最高赏金的大臣 每一个大臣获得的赏金 61 国王的左手 前面所有大臣的左手乘积 自己的右手 于是这里可以自然的想到贪心 xff1a 尽可能让前面所有人的乘积更小 xff0c 而自己的右手又尽可能地
  • 1.1 Qt Creater使用Python开发桌面软件的操作流程

    Qt Creater及Python的下载与安装过程不再赘述 xff0c 读者可自行在网上搜索相应的下载与安装方法 首先我们打开Qt Creater xff0c 单击 Create Project 按钮或单击菜单栏中的 文件 New Proj
  • zootracer使用说明——一款视频物体追踪软件,获取运动物体在屏幕坐标系的运动轨迹

    警告 xff01 软件会使用大量计算机资源 xff0c 请使用配置较高的电脑运行程序 xff01 不然容易把电脑跑坏 xff01 我的配置 xff1a CPU AMD Ryzen 7 5800H with Radeon Graphics G
  • Dockerfile概念简介

    Dockerfile概念简介 前言一 dockerfile概念二 Docker镜像的创建 1 基于现有镜像创建 2 基于本地模板创建 3 基于dockerfile创建 dockerfile结构 xff08 四部分 xff09 构建镜像命令
  • Android:file.mkdirs() false

    如果创建文件目录失败 就要考虑两个原因 1 是否给了读写权限 清单文件有读写权限 但是创建目录之前是否允许了 span class token operator lt span uses span class token operator
  • 【Flutter web】内网网站如何发布?解决外网下canvaskit.js和字体无法加载问题

    背景 由于部署的网站只能在内网下使用 xff0c 部署服务器又不能访问外网 xff0c 导致Flutter web部署遇到很多问题 xff0c 比如 xff1a 白屏 部署的网站为何首次加载缓慢 xff0c 会白屏 xff1f 通过浏览器开
  • 【Flutter web】实现批量生成可下载的二维码,二维码图片点击下载

    这里写自定义目录标题 先看效果 xff1a 方法 xff1a 先看效果 xff1a 方法 xff1a web布局就略过 xff0c 自行练习 xff0c 只讲重点 xff01 此项目需要用到三个依赖库 xff1a zxing2 0 1 0i
  • android 实现类似个人中心的界面设计

    上效果图 xff1a 先理清设计思路 xff1a 1 外层用linearlayout包裹 xff0c linearlayout采用shape 搭上描边 圆角和填充背景色 2 里层采用relativelayout填充进textview ima
  • 上传项目到github并供团队克隆

    github注册就不说了 1 创建仓库 创建后就能在首页中看到创建的仓库名了 2 本地克隆仓库 到github的项目仓库中找到项目的地址 xff0c 如第一图 磁盘中创建项目文件夹作为仓库 xff0c 右键选择torToiseGit gt
  • Fragment和Activity两种沉浸式状态栏的实现

    我们普通的Activity所有的标题栏颜色风格基本是一致的 xff0c 所以我们可以将这种单独的Activity的沉浸式状态栏放在BaseActivity中实现 但是如果遇到一级栏目的fragment中 xff0c 且有些fragment中
  • Android Studio Git实现回退至某一个版本

    流程 xff1a 一 android studio上部VCS gt Git gt Reset Head 二 选择Reset Type 注释 xff1a Reset Type git reset mixed xff1a 此为默认方式 xff0
  • Gitlab给指定人员设置指定权限

    1 选中指定的项目 xff0c 再选择Members 2 选择要指定的人员 xff0c 选择Project Access xff0c 为其添加指定的权限 xff0c 添加
  • 1.5 Qt Creater使用Python开发桌面软件的程序打包

    当我们开发完成软件后 xff0c 如果需要分发到其它电脑上运行 xff0c 我们需要进行程序打包 通过程序打包 xff0c 我们可以方便其他用户在其它设备上进行程序的使用 最简单且打包文件最小的方式为 xff1a 我们将开发用到的Pytho
  • Vue--Module parse failed: Unexpected character '' (1:0) (fonts/element-icons.ttf)

    当Vue引入iview Element ui后 xff0c npm run dev报错如下图 xff1a 本人项目采用webpack打包工具 xff0c 由于webpack打包工具是将浏览器不能直接运行的拓展语言 xff08 Scss xf
  • 安卓获取APP对应的Android id的原理分析

    android id 的生成原理是由系统生成的随机数 xff0c 并与应用 app 签名 xff0c 经过 HmacSHA256 算法生成的 xff1b 从 android 8 以后开始就是随机的了 xff0c 每个应用获取到的简要步骤 x