如何使用隐藏和内部API构建Android SDK?

89

我想重新构建Android SDK(或者只是android.jar),以包含隐藏和内部API。

我没有找到任何关于如何进行此操作的文档或讨论。我已经设置了一个Ubuntu CyanogenMod构建环境,可以构建cm7。

现在,我了解到使用make SDK可以构建SDK,但我想构建一个包含被标记为@hide的方法和字段的SDK。这可行吗?

我想要做的是更改使用隐藏API的应用程序,并为了重新构建它,我想使用修改后的SDK。


2
@Hidden 只是隐藏了 Javadoc,所有这些方法仍然可用。 - Blundell
12
@hide 从类文件中移除它们。 - Thomas Hofmann
1
能否使用带有@Hide注释的Android SDK方法? - Blundell
4
我认为您可以删除要访问的API上的 @Hidden 标签,然后执行 make update-apimake SDK 来构建自己的SDK。 - dreamtale
理论上,您可以编写一个脚本来解析SDK中的所有文件,并删除所有@hide标签。当然,这需要一些时间。然后重新构建SDK。 - YanDaik
显示剩余6条评论
10个回答

69

这对我也起作用了!现在我可以继续实际任务了,感谢您。 - CurlyPaul
在我看来,这是最简单和最快的方法。 - Patrick Cho
有没有办法将此标记为正确答案? - Chris Browet
2
这个方法适用于所有设备吗,还是只适用于目标设备? - Hải Phong
要在Android Studio中定义库的顺序,您需要修改模块的.iml文件,并将所需库的<orderEntry>放在Android SDK之前。不幸的是,这种技术不是持久的,因为一旦按下gradle-sync按钮,该文件就会被覆盖。 - waqaslam
显示剩余3条评论

45

我进行了一些调查,我的结论是: 这不可能只花费很少的工作量就能完成。 阅读本答案的其余部分以获取我所发现的详细信息。


android.jar 实际上由设备中 system/frameworks/ 目录中的 framework.jarcore.jar 的 "公共 API" 组成。 android.jar 可以看作是 Java 库头文件,其中实际字节码实现只是一个 throw new RuntimeException("stub");,这允许您针对 android.jar 进行构建(例如在 Eclipse 中),但必须在设备或模拟器上执行。

Android SDK 的公共 API 由未带有 @{hide} javadoc 注释前缀的类/方法/字段定义。即未带注释的所有内容都包含在 SDK 中。

android.jar 是从位于 out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates 的源代码构建而成,该源代码本身是由位于 build/tools/droiddoc 的工具 DroidDoc 生成的。

DroidDoc 是生成实际 Android SDK 文档的工具(可能从 javadoc 改编而来,或使用 javadoc)。作为一个副作用,并可能是因为它已经解析了所有 javadoc,它还会产生 Android 桩,然后编译成分发在 SDK 中的 android.jar

因此,如果您只想包含特定部分的隐藏内容,则可以删除 @hide 注释并重新构建 SDK。

然而,如果您想包含所有隐藏的部分,情况会变得更加复杂。您可以修改 DroidDoc(相关源代码在 build/tools/droiddoc/src/Stubs.java 中),使其不将任何内容检测为隐藏。这很简单,我已经尝试过了,但是生成的存根无法编译。

我的结论是:这根本不可行。如果删除 DroidDoc 检测隐藏注释的部分,则生成的存根根本无法编译,并且需要相当多的工作才能正确编译。

所以我的答案是:不,这需要进行大量的工作才能完成。抱歉。


关于 mkstubs 工具的一些说明。 mkstubs 用于构建 SDK 插件,即您可以从供应商处找到的 Android SDK 管理器中的插件,例如 Samsung 为您提供了一个额外的 API,针对 Samsung 手机的特定功能。 mkstubs 基本上与 DroidDoc 存根生成过程相同,但它不使用 @hide 注释,而是使用一个 .defs 文件来描述要包括或排除哪些包/类/字段。

然而,这与问题无关,因为 Android SDK 构建不使用 mkstubs 工具。(不幸的是。)


我也看了一下。除了Droiddoc之外,还有一个叫做/development/tools/mkstubs的工具。在构建过程中会调用它,据我所见,它会修改类文件并将其截断。在build/core/tasks/sdk-addon.mk中,有以下代码:定义stub-addon-jar $(call stub-addon-jar-file,$(1)): $(1) | mkstubs $(info Stubbing addon jar using $(PRODUCT_SDK_ADDON_STUB_DEFS)) $(hide) java -jar $(call module-installed-files,mkstubs) $(if $(hide),,--v)
"$$<" "$$@" @$(PRODUCT_SDK_ADDON_STUB_DEFS) endef
- Thomas Hofmann
很不幸,我对这个东西不太了解,但在我看来,它似乎完全取决于一个名为hide的变量。虽然我还没有找到它被设置的地方。如果你在其他构建文件中搜索$(hide),你会发现很多都依赖于这个值。它似乎也会影响C库的构建方式。 - Thomas Hofmann
@ThomasHofmann:我最初也对mkstubs工具感到困惑。我所学到的是,只有在构建(供应商)sdk插件时才会使用mkstubs,而不是在构建普通sdk时使用。然而,mkstubs与DroidDoc做的事情基本相同,只是它不使用“@hide”注释,而是使用一个“.defs”文件来描述插件API中要包含哪些包/类/字段。 - Bjarke Freund-Hansen
2
@ThomasHofmann:关于$(hide),你自己把自己搞糊涂了。 $(hide)仅仅是用于makefile中的前缀,用于隐藏正在执行的程序的实际命令行,仅此而已,并且几乎可以在任何地方使用。它与Android SDK或源代码中的@hide注释完全没有任何关系。 - Bjarke Freund-Hansen
我该如何检查上述路径 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar? - Bunny

31

我们可以从Android平台重建*.jar文件。

首先,将ADB连接到您的设备。然后运行:

adb pull /system/framework/core.jar .
adb pull /system/framework/framework.jar .

core.jar 包含标准的 Java 库 (java.*),而 framework.jar 包含 Android 库 (android.*)。但实际上这些文件并不是 JAR 格式的,而是 DEX 格式的,因此无法直接使用。

我们可以使用工具(如dex2jar)将这些以 DEX 格式编译的 *.jars 转换成真正的 JARs:

dex2jar core.jar
dex2jar framework.jar

假设你正在使用Eclipse ADT,则使用“添加外部JAR文件...”来导入这些JAR文件。

  • 右键单击项目 → 属性 → Java Build Path → 库 → 添加外部JAR文件... →(从上面选择core-dex2jar.jarframework-dex2jar.jar)。

这将使您能够使用内部和一些Java 7 API。(据我所见,生成的APK不包含任何来自JAR文件的实际代码。)


非常感谢,我尝试了很多方法,只有你的方法对我有效。 - SalutonMondo
7
需要注意的是,这种方法仍然适用于ICS及更高版本的系统,但需要进行更多的操作。相关文件包括/system/framework/core.odex/system/framework/framework.odex等等。这些文件可以被反Odex化(java -jar baksmali-2.0.3.jar -d system.framework -x system.framework/core.odex -o core)和重新Odex化(java -jar smali-2.0.3.jar -x -o core.dex core),只有在此之后才能使用dex2jar core.dex完成其工作。 - Alex Cohn
无法在Marshmallow中找到core.jar。 - mehmet6parmak
你有没有关于Android P中核心和框架jar的想法? - Prabhakaran
我该如何检查上述路径 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar? - Bunny
我认为这些在普通设备上不会起作用,我们需要root访问或AOSP设备才能使框架文件夹存在于文件系统中。 - babbin tandukar

21

你可以从这个代码库下载修改过的android.jar,用作隐藏的API。请按照那里的说明操作。


4
这个答案因为它是最简单的解决方案,应该得到更多的赞。如果你正在寻找一个解决方案,请知道其他人已经这样做并将解决方案放在了 Github 仓库中。使用它并享受吧!)) - Mixaz
1
在使用API 28的android.jar时,我在robolectric中遇到了一个错误。已经提出了一个问题 https://github.com/anggrayudi/android-hidden-api/issues/62 ,感谢@Anggrayudi提供的jar文件。 - Prabhakaran
我也尝试了在Android 10上进行相同的操作,但是没有成功。后来我从其他贡献者那里下载了 https://github.com/aeab13/android-jar-with-hidden-api 。我能够访问HDMI-CEC类并构建它,但代码仍然很混乱。代码链接:https://android.googlesource.com/platform/frameworks/base/+/4e90fcd/core/java/android/hardware/hdmi - babbin tandukar

18

Lollipop的流程略有不同:

  1. 从Lollipop设备获取 /system/framework/arm/boot.oat

  2. 使用 'java -jar oat2dex.jar boot boot.oat'

  3. 你会得到两个文件夹:dex和odex。进入dex文件夹,运行'java -jar dex2jar.jar framework.dex'
  4. 将结果重命名为framework.jar后解压缩并找到所需的类
  5. 前往[sdk_path]/platforms/[target_platform]并提取android.jar(首先将其重命名为zip)。
  6. 将提取的framework中的文件复制到提取的android.jar中。然后压缩成zip并将其重命名为.jar :)

ps:也许您需要为'framework_classes2.dex'重复步骤4-6


对于第三步,我在那个链接中找不到dex2jar.jar...我尝试了很多方法,但是我无法弄清楚。有没有其他地方可以找到它的链接?它是否位于我的Android SDK中?我找不到它。 - Dwebtron
1
请查看“发布”部分。 - deviant
我已经查看了那里,除非我漏掉了什么,否则有很多以“.jar”结尾的文件,但是没有像你在评论中提到的“dex2jar.jar”。当我尝试运行这些文件中的任何一个(根据您的评论),我会收到一个错误,指出“<filename>中没有主清单属性”。那个git页面上的说明说要构建以.apk结尾的东西... 我已经尝试过了,但是#1会生成以.apk结尾而不是.jar的文件,#2无法正常工作。我是否看到/做错了什么?我对此还比较陌生... - Dwebtron
2
看起来最近的dex2jar版本已经改变了下载格式。只需解压缩下载文件,然后直接在framework.dex文件上运行脚本“d2j-dex2jar.sh”或“d2j-dex2jar.bat”,而不是使用“java -jar ...”。根据您的平台选择相应的脚本即可。 - CalumMcCall
1
我已将两个jar文件复制到android.jar中,现在Android Studio告诉我Error:Execution failed for task ':app:processDebugResources'。
com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'D:\Programme\Android\android-sdk\build-tools\19.1.0\aapt.exe'' finished with non-zero exit value 1
- wutzebaer
显示剩余4条评论

14

DroidCon 2011

这里,Sony Ericsson的Erik Hellman解释了如何访问隐藏的Android API:

http://vimeo.com/30180393(链接似乎无法使用)。

前往DroidCon网页Day 2,向下滚动到Using Hidden APIs 10:15,就可以在那里观看。

链接正在失效!

我找到了这个:http://skillsmatter.com/podcast/os-mobile-server/hidden-api,但我不知道它能存在多久。

Android SDK中的官方API通常对大部分普通应用足够。然而,有时候开发人员需要访问内部系统服务、API和资源,而这些内容并未发布在官方API中。幸运的是,通过一些巧妙的技巧,这些API仍然可以被访问,并且在开发基于Android的新颖解决方案时经常会派上用场。在本次课程中,您将学习如何访问和使用这些隐藏和受保护的API,它们的使用限制以及如何在多个厂商设备和Android版本上以安全和可控的方式使用它们的一些技巧。观众将看到几个先进的演示,在Android中通常无法实现。期望一次相当高级的会议,带有对Android平台内部工作原理的深入见解。


1
很有趣,但不幸的是它并没有回答我的问题。我正在寻找一种将隐藏内容包含在内的重建SDK的方法。 - Thomas Hofmann
看起来我找到了另一种实现我想要的方式。明天我会描述它。 - Thomas Hofmann
我发现如果你想编译使用ADT中隐藏API的项目源代码,可以按照以下步骤进行:1)为源代码创建一个Android项目。 2)从构建路径中删除Android类路径容器。 3)定义一个用户库(也勾选系统库复选框),其中包括来自ASOP ROM构建(例如cm7)的JAR文件。你所需引用的JAR文件取决于你需要参考什么。framework-immediates可能是其中的一部分。 - Thomas Hofmann
当项目构建完成后,用户库类将不会包含在正在创建的 APK 中。隐藏的 API 是可见的,一切都能够正常编译。 - Thomas Hofmann
@Blundell:你能更新一下链接吗?它们已经失效了! - zombie
我该如何检查上述路径 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar? - Bunny

12
尝试查看this
这些文章的最终目标是让开发者在不使用反射的情况下获得内部和隐藏API的能力。如果您按照接下来几个部分描述的所有步骤完成,您将能够像使用公共开放API一样使用内部和隐藏API,而无需使用反射。但是,如果您正在使用这些非公开API,则应该意识到您的应用程序面临很大的风险。基本上,没有保证API不会在Android操作系统的下一个更新中被破坏。甚至没有保证来自不同供应商的设备之间的行为一致性。您完全是自己的。有三种情况您可能想要遵循:启用内部和隐藏API(场景A)、仅启用隐藏API(场景B)或仅启用内部API(场景C)。场景A是B和C的总和。场景B是最简单的(不需要修改eclipse ADT插件)。场景A:阅读第12345部分。场景B:阅读第1235部分。场景C:阅读第12345部分。

3
我已经阅读了那篇博客文章,它提到了以下内容: "1) Android是一个开源项目。我们可以下载源代码并自定义构建系统,以便android.jar不排除内部和隐藏类。但这是一种困难的方法。"不幸的是,它没有详细说明。 - Thomas Hofmann
3
请前往博客文章的最后一部分(https://devmaze.wordpress.com/2011/01/19/using-com-android-internal-part-5-summary-and-example/),那里有一个链接(https://github.com/inazaruk/android-sdk/tree/master/platforms),其中包含所有隐藏和内部API的预构建Android API。 - Bob
1
给定的链接无法访问,需要作者的许可。 - SHAHS
我该如何检查上述路径 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar? - Bunny
这些链接已不再对公众开放。 - Lucas

1

Long的回答对我很有帮助,但我仍然缺少一些我需要的类,特别是android.provider.Telephony。我可以通过以下方式添加它:

  1. 提取framework.jar文件

    mkdir /tmp/framework
    cp framework.jar /tmp
    cd /tmp/framework
    jar xvf ../framework.jar
    mv android classes
    
  2. 构建Android repo,这将创建out/target/common/obj/JAVA_LIBRARIES目录

  3. 查找缺失的类所在位置

    $ cd /path/to/out/target/common/obj/JAVA_LIBRARIES
    $ find . | grep "/Telephony.class"
    ./telephony-common_intermediates/classes/android/provider/Telephony.class
    ./android_stubs_current_intermediates/classes/android/provider/Telephony.class
    
  4. 添加新类并重新构建框架JAR文件

    cd /tmp/framework
    cp -r /path/to/out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes .
    cp -r /path/to/out/target/common/obj/JAVA_LIBRARIES/telephony-common_intermediates/classes .
    cd classes
    jar cvf ../framework.jar .
    

或者你可以变得懒惰,将所有类都包含在一个巨大的jar文件中:

cd /tmp/framework
cp -r /path/to/out/target/common/obj/JAVA_LIBRARIES/*/classes .
cd classes
jar cvf ../framework.jar .

如何找到 out/target/common/obj/JAVA_LIBRARIES 这个路径? - Bunny
@Bunny 我已经更新了我的回答并提供了更多细节。但是这里的答案似乎更容易理解:https://dev59.com/wWsz5IYBdhLWcg3wcndb#32626155 - bmaupin
感谢您的回复@bmaupin..您能否帮助我找出这个路径out/target/common/obj/JAVA_LIBRARIES,以获取框架jar文件...我真的卡住了..我找不到这个路径或者缺少什么..请指导。 - Bunny

1
我曾经写过一些Groovy脚本,用于从http://source.android.com/的repo checkout中提取java文件,并在不需要编译所有android源代码的完整工具链的情况下进行编译,包括其他必要步骤(打包、生成资源等)。
这些脚本可以在此处找到:

https://github.com/thoutbeckers/CollectAndroid

但是肯定需要更新任何姜饼之后的内容,主要是通过在配置文件(CollectConfig.groovy)中设置“rootdirs”的正确目录来实现。同时涉及到所有隐藏API和源码(当时也有些问题)的开发。

正如其他地方提到的那样,由于添加的访问规则,com/android/internal/**仍将在最新版本的ADT中保持隐藏状态。


我该如何检查上述路径 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar? - Bunny
我归档了那个仓库,因为它最后更新是针对_Gingerbread_。不幸的是,你必须找到另一种方法。 - thoutbeckers

0

我不能评论,但这基本上是对@KennyTM(https://dev59.com/wWsz5IYBdhLWcg3wcndb#13550030)出色答案的评论:

如果你在Eclipse中遇到以下错误:

The type com.android.internal.util.Predicate cannot be resolved. It is indirectly referenced from required .class   files

也就是说,android.internal.* 是不可用的。

那么一个可能的解决方案是针对 / system / framework / framework2.jar 应用相同的方法。 在SDK19的Android模拟器中,我有这个额外的jar包。 在我的HTC One手机上甚至有一个framework3.jar。


我怎样才能检查此路径 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar? - Bunny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接