最近在研究安Android 源码的时候偶然发现,在framework中有一个如下的提交,让人感觉比较有意思
字面意思比较直接:不让或者限制使用非公开的api方法(也就算@hide 注塑的public方法).根据这个提交,顺带研究了下大部分提交。原理,水平有限没太看懂。
commit 927d6de11fa038ee78bb90933eee3ebc20b68751
Author: David Brazdil <dbrazdil@google.com>
Date: Wed Jan 24 19:54:30 2018 +0000
Show a warning toast/dialog when an app uses hidden APIs
Check VMRuntime.hasUsedHiddenApi() on each Activity start and show
a toast/dialog urging the user to check logcat.
Test: manual
Bug: 64382372
Change-Id: Ida8a6ed9ab9b56a76882501b2a3473a5f1448cb3
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index dbdb81c..2e7a0ed 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -128,6 +128,8 @@ import com.android.internal.app.WindowDecorActionBar;
import com.android.internal.policy.DecorView;
import com.android.internal.policy.PhoneWindow;
+import dalvik.system.VMRuntime;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -7039,11 +7041,12 @@ public class Activity extends ContextThemeWrapper
mFragments.dispatchStart();
mFragments.reportLoaderStart();
- // This property is set for all builds except final release
- boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
boolean isAppDebuggable =
(mApplication.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+ // This property is set for all builds except final release
+ boolean isDlwarningEnabled = SystemProperties.getInt("ro.bionic.ld.warning", 0) == 1;
+
if (isAppDebuggable || isDlwarningEnabled) {
String dlwarning = getDlWarning();
if (dlwarning != null) {
@@ -7064,6 +7067,28 @@ public class Activity extends ContextThemeWrapper
}
}
+ // We might disable this for final builds.
+ boolean isApiWarningEnabled = true;
+
+ if (isAppDebuggable || isApiWarningEnabled) {
+ if (VMRuntime.getRuntime().hasUsedHiddenApi()) {
+ String appName = getApplicationInfo().loadLabel(getPackageManager())
+ .toString();
+ String warning = "Detected problems with API compatiblity\n"
+ + "(please consult log for detail)";
+ if (isAppDebuggable) {
+ new AlertDialog.Builder(this)
+ .setTitle(appName)
+ .setMessage(warning)
+ .setPositiveButton(android.R.string.ok, null)
+ .setCancelable(false)
+ .show();
+ } else {
+ Toast.makeText(this, appName + "\n" + warning, Toast.LENGTH_LONG).show();
+ }
+ }
+ }
+
mActivityTransitionState.enterReady(this);
}
以下的跟人见解是基于初步研读了当前(截止2018/02/03)的关于限制使用hide api的方案之后的。不一定正确,
基于源码环境编译的app
这种也就是我们俗称的手机厂商、ODM厂商通常会遇到的情况
是如何限制使用hide api的?
对此专门开发了一个名为hiddenapi的工具,在并且在编译生成odex文件的时候,会去check是否有使用你hide 标注的public方法。
如何绕过该限制呢?
初步看应该是可以通过config文件类进行配置,类似private-runtime-permission.xml这种
配置文件的路径:framework/base/config/, 可以看到hiddenapi-blacklist.txt hiddenapi-dark-greylist.txt两个文件。
对应的代码提交
define hiddenapi-copy-dex-files
$(2): $(1) $(HIDDENAPI) $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
@rm -rf $(dir $(2))
@mkdir -p $(dir $(2))
find $(dir $(1)) -maxdepth 1 -name "classes*.dex" | sort | \
xargs -I{} cp -f {} $(dir $(2))
find $(dir $(2)) -name "classes*.dex" | sort | sed 's/^/--dex=/' | \
xargs $(HIDDENAPI) --light-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
--dark-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
--blacklist=$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
endef
define hiddenapi-copy-soong-jar
$(2): PRIVATE_FOLDER := $(dir $(2))dex-hiddenapi
$(2): $(1) $(HIDDENAPI) $(SOONG_ZIP) $(MERGE_ZIPS) $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
@echo "Hidden API: $$@"
$$(copy-file-to-target)
@rm -rf $${PRIVATE_FOLDER}
@mkdir -p $${PRIVATE_FOLDER}
unzip -q $(2) 'classes*.dex' -d $${PRIVATE_FOLDER}
find $${PRIVATE_FOLDER} -name "classes*.dex" | sort | sed 's/^/--dex=/' | \
xargs $(HIDDENAPI) --light-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
--dark-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
--blacklist=$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
$(SOONG_ZIP) -o $${PRIVATE_FOLDER}/classes.dex.jar -C $${PRIVATE_FOLDER} -D $${PRIVATE_FOLDER}
$(MERGE_ZIPS) -D -zipToNotStrip $${PRIVATE_FOLDER}/classes.dex.jar -stripFile "classes*.dex" \
$(2) $${PRIVATE_FOLDER}/classes.dex.jar $(1)
endef
framework层提示使用了隐藏的api的控制属性
# -----------------------------------------------------------------
# Enable dynamic linker and hidden API developer warnings for
# userdebug, eng and non-REL builds
ifneq ($(TARGET_BUILD_VARIANT),user)
ro.art.hiddenapi.warning=1
else
# Enable it for user builds as long as they are not final.
ifneq ($(PLATFORM_VERSION_CODENAME),REL)
ro.art.hiddenapi.warning=1
endif
endif
上传两张相关的代码片段
基于Google release的SDK所编写的app
者在也就所我们通常在各个应用市场上所下载的app.
通过上一步的分析,可以看出,其实在install独立的应用时,也是会check是否使用了非公开的api的;这对于后续的app开发也算是指明了个大体的方向把。
建议:
- ODM、Android设备厂商禁止使用make update-api 这种方式来动态的增加暴露给外部的api函数。所有framework新增的public方法一律添加@hide注释。
- 后续可能存在的风险,例如无法测试通过cts
- 无法正常升级等等
- 规范使用android 所提供的接口信息。让你做什么你就做什么
- 反射、等通过各种技术手段所绕过的独立的app,要注意了。
- 个人猜测目前你应该是有大量的应用直接或者间接的使用了个字hide方法