从 Android 9(API 级别 28)开始,此平台对应用能使用的非 SDK 接口实施了限制。只要应用引用非 SDK 接口或尝试使用反射或 JNI 来获取其句柄,这些限制就适用。这些限制旨在帮助提升用户体验和开发者体验,为用户降低应用发生崩溃的风险,同时为开发者降低紧急发布的风险。Google官方文档地址:针对非 SDK 接口的限制
AMS有一个HiddenApiSettings类型的成员,负责记录黑名单是否使能(mBlacklistDisabled),hiddenapi豁免名单信息(mExemptionsStr,mExemptions)和hiddenapi限制政策(mPolicy)等信息。HiddenApiSettings通过registerObserver()监听Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS(hiddenapi豁免名单信息)和Settings.Global.HIDDEN_API_POLICY(hiddenapi限制政策)的变化从而调用update()更新内部信息。
update()函数读取Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS的值,如果是“*”,则所有hiddenapi都获得豁免,黑名单不使能;如果是单一方法(域)签名或者以逗号分隔开来的方法(域)签名,则把它们保存到mExemptions中,通过“–set-api-blacklist-exemptions”加签名的形式传递给zygote,以便在虚拟机中进行设置,例如,通过adb shell settings put global hidden_api_blacklist_exemptions Lorg/apache/xpath/NodeSetDTM;->setItem(II)V可以为该方法加入到hiddenapi豁免名单。然后读取Settings.Global.HIDDEN_API_POLICY的值,将hiddenapi限制政策设置成这个值。
这个值可以取HIDDEN_API_ENFORCEMENT_DEFAULT,HIDDEN_API_ENFORCEMENT_DISABLED,HIDDEN_API_ENFORCEMENT_JUST_WARN,HIDDEN_API_ENFORCEMENT_ENABLED。
HIDDEN_API_ENFORCEMENT_DEFAULT:表示是否限制该hiddenapi取决于targetsdk。
HIDDEN_API_ENFORCEMENT_DISABLED:表示完全禁用hiddenapi限制。
HIDDEN_API_ENFORCEMENT_JUST_WARN:表示禁用hiddenapi限制但是会打印警告log。
HIDDEN_API_ENFORCEMENT_ENABLED:表示深灰名单和黑名单里面的hiddenapi会被限制。
如需允许访问非 SDK 接口,请使用以下 adb 命令:adb shell settings put global hidden_api_policy 1;如需将 API 强制执行策略重置为默认设置,请使用以下命令:adb shell settings delete global hidden_api_policy。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
static class HiddenApiSettings extends ContentObserver
implements DeviceConfig.OnPropertiesChangedListener {
private final Context mContext;
private boolean mBlacklistDisabled;
private String mExemptionsStr;
private List mExemptions = Collections.emptyList();
private int mLogSampleRate = -1;
private int mStatslogSampleRate = -1;
@HiddenApiEnforcementPolicy private int mPolicy = HIDDEN_API_ENFORCEMENT_DEFAULT;
/**
* Sampling rate for hidden API access event logs with libmetricslogger, as an integer in
* the range 0 to 0x10000 inclusive.
*
* @hide
*/
public static final String HIDDEN_API_ACCESS_LOG_SAMPLING_RATE =
"hidden_api_access_log_sampling_rate";
/**
* Sampling rate for hidden API access event logging with statslog, as an integer in the
* range 0 to 0x10000 inclusive.
*
* @hide
*/
public static final String HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE =
"hidden_api_access_statslog_sampling_rate";
public void onPropertiesChanged(DeviceConfig.Properties properties) {
int logSampleRate = properties.getInt(HIDDEN_API_ACCESS_LOG_SAMPLING_RATE,
mLogSampleRate);
int statslogSampleRate = properties.getInt(HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE,
mStatslogSampleRate);
setSampleRates(logSampleRate, statslogSampleRate);
}
private void setSampleRates(int logSampleRate, int statslogSampleRate) {
if (logSampleRate >= 0 && logSampleRate <= 0x10000
&& logSampleRate != mLogSampleRate) {
mLogSampleRate = logSampleRate;
ZYGOTE_PROCESS.setHiddenApiAccessLogSampleRate(mLogSampleRate);
}
if (statslogSampleRate >= 0 && statslogSampleRate <= 0x10000
&& statslogSampleRate != mStatslogSampleRate) {
mStatslogSampleRate = statslogSampleRate;
ZYGOTE_PROCESS.setHiddenApiAccessStatslogSampleRate(mStatslogSampleRate);
}
}
/**
* Set initial sampling rates from DeviceConfig. This is required after each restart,
* if they never get updated.
*/
private void initializeSampleRates() {
int logSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT,
HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, 0);
int statslogSampleRate = DeviceConfig.getInt(DeviceConfig.NAMESPACE_APP_COMPAT,
HIDDEN_API_ACCESS_STATSLOG_SAMPLING_RATE, 0);
setSampleRates(logSampleRate, statslogSampleRate);
}
public HiddenApiSettings(Handler handler, Context context) {
super(handler);
mContext = context;
}
public void registerObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS),
false,
this);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY),
false,
this);
initializeSampleRates();
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_APP_COMPAT,
mContext.getMainExecutor(), this);
update();
}
private void update() {
String exemptions = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS);
if (!TextUtils.equals(exemptions, mExemptionsStr)) {
mExemptionsStr = exemptions;
if ("*".equals(exemptions)) {
mBlacklistDisabled = true;
mExemptions = Collections.emptyList();
} else {
mBlacklistDisabled = false;
mExemptions = TextUtils.isEmpty(exemptions)
? Collections.emptyList()
: Arrays.asList(exemptions.split(","));
}
if (!ZYGOTE_PROCESS.setApiBlacklistExemptions(mExemptions)) {
Slog.e(TAG, "Failed to set API blacklist exemptions!");
// leave mExemptionsStr as is, so we don't try to send the same list again.
mExemptions = Collections.emptyList();
}
}
mPolicy = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY);
}
private @HiddenApiEnforcementPolicy int getValidEnforcementPolicy(String settingsKey) {
int policy = Settings.Global.getInt(mContext.getContentResolver(), settingsKey,
ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT);
if (ApplicationInfo.isValidHiddenApiEnforcementPolicy(policy)) {
return policy;
} else {
return ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
}
}
boolean isDisabled() {
return mBlacklistDisabled;
}
@HiddenApiEnforcementPolicy int getPolicy() {
return mPolicy;
}
public void onChange(boolean selfChange) {
update();
}
}
在AMS创建进程的过程中,会将携带hiddenapi信息的runtimeflags传递给Zygote从而fork出进程。如果黑名单不使能(即没有通过adb shell settings put global hidden_api_blacklist_exemptions *设置过),则可以单独为该应用进程设置独立的hiddenapi限制政策,保存在ApplicationInfo的mHiddenApiPolicy中,有默认值HIDDEN_API_ENFORCEMENT_DEFAULT。
frameworks/base/services/core/java/com/android/server/am/ProcessList.java
@GuardedBy("mService")
boolean startProcessLocked(ProcessRecord app, HostingRecord hostingRecord,
boolean disableHiddenApiChecks, boolean disableTestApiChecks,
boolean mountExtStorageFull, String abiOverride) {
...
if (!disableHiddenApiChecks && !mService.mHiddenApiBlacklist.isDisabled()) {
app.info.maybeUpdateHiddenApiEnforcementPolicy(
mService.mHiddenApiBlacklist.getPolicy());
@ApplicationInfo.HiddenApiEnforcementPolicy int policy =
app.info.getHiddenApiEnforcementPolicy();
int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
if ((policyBits & Zygote.API_ENFORCEMENT_POLICY_MASK) != policyBits) {
throw new IllegalStateException("Invalid API policy: " + policy);
}
runtimeFlags |= policyBits;
if (disableTestApiChecks) {
runtimeFlags |= Zygote.DISABLE_TEST_API_ENFORCEMENT_POLICY;
}
}
...
}
maybeUpdateHiddenApiEnforcementPolicy会尝试将HiddenApiSettings里面的全局的hiddenapi限制政策赋给应用进程。getHiddenApiEnforcementPolicy会尝试获取应用进程的hiddenapi限制政策。
如果在/system/etc/sysconfig/hiddenapi-package-whitelist.xml文件有添加该应用作为hiddenapi白名单的话,则isPackageWhitelistedForHiddenApis()返回true。isAllowedToUseHiddenApis()在1.应用进程为系统签名;2.应用为/system下面或者是经过/system下面应用更新而来的,且配置了android:usesNonSdkApi=true属性;3.应用为/system下面或者是经过/system下面应用更新而来的,且在/system/etc/sysconfig/hiddenapi-package-whitelist.xml文件配置了白名单 满足这三种情况之一返回true。
换句话说,当isAllowedToUseHiddenApis()返回true是,传给Zygote的是HIDDEN_API_ENFORCEMENT_DISABLED;isAllowedToUseHiddenApis()返回false且全局的hiddenapi限制政策是HIDDEN_API_ENFORCEMENT_DEFAULT时,传给Zygote的是HIDDEN_API_ENFORCEMENT_ENABLED;其他情况传给Zygote的就是全局的hiddenapi限制政策。
/frameworks/base/core/java/android/content/pm/ApplicationInfo.java
public void maybeUpdateHiddenApiEnforcementPolicy(@HiddenApiEnforcementPolicy int policy) {
if (isPackageWhitelistedForHiddenApis()) {
return;
}
setHiddenApiEnforcementPolicy(policy);
}
...
public @HiddenApiEnforcementPolicy int getHiddenApiEnforcementPolicy() {
if (isAllowedToUseHiddenApis()) {
return HIDDEN_API_ENFORCEMENT_DISABLED;
}
if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
return mHiddenApiPolicy;
}
return HIDDEN_API_ENFORCEMENT_ENABLED;
}
虚拟机在fork出进程后,通过SetHiddenApiEnforcementPolicy设置进程内部虚拟机的hiddenapi限制政策,保存到Runtime的hidden_api_policy_。
art/runtime/native/dalvik_system_ZygoteHooks.cc
static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
jclass,
jlong token,
jint runtime_flags,
jboolean is_system_server,
jboolean is_zygote,
jstring instruction_set) {
...
hiddenapi::EnforcementPolicy api_enforcement_policy = hiddenapi::EnforcementPolicy::kDisabled;
...
api_enforcement_policy = hiddenapi::EnforcementPolicyFromInt(
(runtime_flags & HIDDEN_API_ENFORCEMENT_POLICY_MASK) >> API_ENFORCEMENT_POLICY_SHIFT);
runtime_flags &= ~HIDDEN_API_ENFORCEMENT_POLICY_MASK;
if ((runtime_flags & DISABLE_TEST_API_ENFORCEMENT_POLICY) != 0u) {
runtime->SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDisabled);
} else {
runtime->SetTestApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
}
runtime_flags &= ~DISABLE_TEST_API_ENFORCEMENT_POLICY;
...
bool do_hidden_api_checks = api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled;
DCHECK(!(is_system_server && do_hidden_api_checks))
<< "SystemServer should be forked with EnforcementPolicy::kDisable";
DCHECK(!(is_zygote && do_hidden_api_checks))
<< "Child zygote processes should be forked with EnforcementPolicy::kDisable";
runtime->SetHiddenApiEnforcementPolicy(api_enforcement_policy);
runtime->SetDedupeHiddenApiWarnings(true);
if (api_enforcement_policy != hiddenapi::EnforcementPolicy::kDisabled &&
runtime->GetHiddenApiEventLogSampleRate() != 0) {
// Hidden API checks are enabled, and we are sampling access for the event log. Initialize the
// random seed, to ensure the sampling is actually random. We do this post-fork, as doing it
// pre-fork would result in the same sequence for every forked process.
std::srand(static_cast(NanoTime()));
}
...
而hiddenapi豁免名单信息则通过SetHiddenApiExemptions设置到Runtime的hidden_api_exemptions_。
art/runtime/native/dalvik_system_VMRuntime.cc
static void VMRuntime_setHiddenApiExemptions(JNIEnv* env,
jclass,
jobjectArray exemptions) {
std::vector exemptions_vec;
int exemptions_length = env->GetArrayLength(exemptions);
for (int i = 0; i < exemptions_length; i++) {
jstring exemption = reinterpret_cast(env->GetObjectArrayElement(exemptions, i));
const char* raw_exemption = env->GetStringUTFChars(exemption, nullptr);
exemptions_vec.push_back(raw_exemption);
env->ReleaseStringUTFChars(exemption, raw_exemption);
}
Runtime::Current()->SetHiddenApiExemptions(exemptions_vec);
}
反射获取成员方法常用的一个方法是getDeclaredMethod。
libcore/ojluni/src/main/java/java/lang/Class.java
public Method getDeclaredMethod(String name, Class>... parameterTypes)
throws NoSuchMethodException, SecurityException {
return getMethod(name, parameterTypes, false);
}
private Method getMethod(String name, Class>[] parameterTypes, boolean recursivePublicMethods)
throws NoSuchMethodException {
if (name == null) {
throw new NullPointerException("name == null");
}
if (parameterTypes == null) {
parameterTypes = EmptyArray.CLASS;
}
for (Class> c : parameterTypes) {
if (c == null) {
throw new NoSuchMethodException("parameter type is null");
}
}
Method result = recursivePublicMethods ? getPublicMethodRecursive(name, parameterTypes)
: getDeclaredMethodInternal(name, parameterTypes);
// Fail if we didn't find the method or it was expected to be public.
if (result == null ||
(recursivePublicMethods && !Modifier.isPublic(result.getAccessFlags()))) {
throw new NoSuchMethodException(getName() + "." + name + " "
+ Arrays.toString(parameterTypes));
}
return result;
}
最终调用到Class_getDeclaredMethodInternal:
art/runtime/native/java_lang_Class.cc
static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis,
jstring name, jobjectArray args) {
ScopedFastNativeObjectAccess soa(env);
StackHandleScope<1> hs(soa.Self());
DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize);
DCHECK(!Runtime::Current()->IsActiveTransaction());
ObjPtr klass = DecodeClass(soa, javaThis);
if (UNLIKELY(klass->IsObsoleteObject())) {
ThrowRuntimeException("Obsolete Object!");
return nullptr;
}
Handle result = hs.NewHandle(
mirror::Class::GetDeclaredMethodInternal(
soa.Self(),
klass,
soa.Decode(name),
soa.Decode>(args),
GetHiddenapiAccessContextFunction(soa.Self())));
if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
return soa.AddLocalReference(result.Get());
}
Class::GetDeclaredMethodInternal的内部实现。简单地说,就是先遍历类中的虚方法集合(public和protected方法集合),找到类名和参数匹配的方法;如果找不到,再遍历类中的直接方法集合(static, private, init方法集合),找到类名和参数匹配的方法。
art/runtime/mirror/class.cc
template
ObjPtr Class::GetDeclaredMethodInternal(
Thread* self,
ObjPtr klass,
ObjPtr name,
ObjPtr> args,
const std::function& fn_get_access_context) {
// Covariant return types (or smali) permit the class to define
// multiple methods with the same name and parameter types.
// Prefer (in decreasing order of importance):
// 1) non-hidden method over hidden
// 2) virtual methods over direct
// 3) non-synthetic methods over synthetic
// We never return miranda methods that were synthesized by the runtime.
StackHandleScope<3> hs(self);
auto h_method_name = hs.NewHandle(name);
if (UNLIKELY(h_method_name == nullptr)) {
ThrowNullPointerException("name == null");
return nullptr;
}
auto h_args = hs.NewHandle(args);
Handle h_klass = hs.NewHandle(klass);
constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kNone;
ArtMethod* result = nullptr;
bool result_hidden = false;
for (auto& m : h_klass->GetDeclaredVirtualMethods(kPointerSize)) {
if (m.IsMiranda()) {
continue;
}
auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize);
// May cause thread suspension.
ObjPtr np_name = np_method->ResolveNameString();
if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) {
if (UNLIKELY(self->IsExceptionPending())) {
return nullptr;
}
continue;
}
bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
if (!m_hidden && !m.IsSynthetic()) {
// Non-hidden, virtual, non-synthetic. Best possible result, exit early.
return Method::CreateFromArtMethod(self, &m);
} else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
// Remember as potential result.
result = &m;
result_hidden = m_hidden;
}
}
if ((result != nullptr) && !result_hidden) {
// We have not found a non-hidden, virtual, non-synthetic method, but
// if we have found a non-hidden, virtual, synthetic method, we cannot
// do better than that later.
DCHECK(!result->IsDirect());
DCHECK(result->IsSynthetic());
} else {
for (auto& m : h_klass->GetDirectMethods(kPointerSize)) {
auto modifiers = m.GetAccessFlags();
if ((modifiers & kAccConstructor) != 0) {
continue;
}
auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize);
// May cause thread suspension.
ObjPtr np_name = np_method->ResolveNameString();
if (np_name == nullptr) {
self->AssertPendingException();
return nullptr;
}
if (!np_name->Equals(h_method_name.Get()) || !np_method->EqualParameters(h_args)) {
if (UNLIKELY(self->IsExceptionPending())) {
return nullptr;
}
continue;
}
DCHECK(!m.IsMiranda()); // Direct methods cannot be miranda methods.
bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
if (!m_hidden && !m.IsSynthetic()) {
// Non-hidden, direct, non-synthetic. Any virtual result could only have been
// hidden, therefore this is the best possible match. Exit now.
DCHECK((result == nullptr) || result_hidden);
return Method::CreateFromArtMethod(self, &m);
} else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
// Remember as potential result.
result = &m;
result_hidden = m_hidden;
}
}
}
return result != nullptr
? Method::CreateFromArtMethod(self, result)
: nullptr;
}
找到名字和参数匹配的方法后,也不是直接返回结果的,还需要通过ShouldDenyAccessToMember函数确认这个方法是否可以被调用者访问。如果可以直接访问,直接通过CreateFromArtMethod返回一个Method;另外,还是用了result变量来保存当前获得的最佳结果,每次找到名字和参数匹配,但是没有权限访问的方法后,会通过IsMethodPreferredOver方法来和之前的最佳结果进行比较,如果新的方法更佳,会将result设置为新的方法。也就是说,Class.cc的内部方法GetDeclaredMethodInternal在能够寻找到签名匹配的方法的前提下,总会返回一个方法。
IsMethodPreferredOver方法的对比原则是:有权限访问的方法由于没有权限访问的方法;直接方法优于虚方法;非合成方法优于合成方法。
art/runtime/mirror/class.cc
ALWAYS_INLINE
static bool IsMethodPreferredOver(ArtMethod* orig_method,
bool orig_method_hidden,
ArtMethod* new_method,
bool new_method_hidden) {
DCHECK(new_method != nullptr);
// Is this the first result?
if (orig_method == nullptr) {
return true;
}
// Original method is hidden, the new one is not?
if (orig_method_hidden && !new_method_hidden) {
return true;
}
// We iterate over virtual methods first and then over direct ones,
// so we can never be in situation where `orig_method` is direct and
// `new_method` is virtual.
DCHECK(!orig_method->IsDirect() || new_method->IsDirect());
// Original method is synthetic, the new one is not?
if (orig_method->IsSynthetic() && !new_method->IsSynthetic()) {
return true;
}
return false;
}
caller_context表示调用者的准入域,callee_context表示被调用者的准入域。虚拟机内部将准入域分成3类:kCorePlatform,kPlatform和kApplication。其中可信程度:kCorePlatform>kPlatform>kApplication。可信程度高的调用者总是可以访问可信程度相对低的被调用者(参考CanAlwaysAccess方法)。
如果可信程度低的调用者访问可信程度高的调用者,则按调用者的准入域来确定是否可以访问。kApplication准入域的访问权限由ShouldDenyAccessToMemberImpl决定,kPlatform准入域的访问权限由HandleCorePlatformApiViolation,kCorePlatform对任意准入域的成员都有访问权限。
art/runtime/mirror/class.cc
template
inline bool ShouldDenyAccessToMember(T* member,
const std::function& fn_get_access_context,
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
// Get the runtime flags encoded in member's access flags.
// Note: this works for proxy methods because they inherit access flags from their
// respective interface methods.
const uint32_t runtime_flags = GetRuntimeFlags(member);
// Exit early if member is public API. This flag is also set for non-boot class
// path fields/methods.
if ((runtime_flags & kAccPublicApi) != 0) {
return false;
}
// Determine which domain the caller and callee belong to.
// This can be *very* expensive. This is why ShouldDenyAccessToMember
// should not be called on every individual access.
const AccessContext caller_context = fn_get_access_context();
const AccessContext callee_context(member->GetDeclaringClass());
// Non-boot classpath callers should have exited early.
DCHECK(!callee_context.IsApplicationDomain());
// Check if the caller is always allowed to access members in the callee context.
if (caller_context.CanAlwaysAccess(callee_context)) {
return false;
}
// Check if this is platform accessing core platform. We may warn if `member` is
// not part of core platform API.
switch (caller_context.GetDomain()) {
case Domain::kApplication: {
DCHECK(!callee_context.IsApplicationDomain());
// Exit early if access checks are completely disabled.
EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
if (policy == EnforcementPolicy::kDisabled) {
return false;
}
// If this is a proxy method, look at the interface method instead.
member = detail::GetInterfaceMemberIfProxy(member);
// Decode hidden API access flags from the dex file.
// This is an O(N) operation scaling with the number of fields/methods
// in the class. Only do this on slow path and only do it once.
ApiList api_list(detail::GetDexFlags(member));
DCHECK(api_list.IsValid());
// Member is hidden and caller is not exempted. Enter slow path.
return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
}
case Domain::kPlatform: {
DCHECK(callee_context.GetDomain() == Domain::kCorePlatform);
// Member is part of core platform API. Accessing it is allowed.
if ((runtime_flags & kAccCorePlatformApi) != 0) {
return false;
}
// Allow access if access checks are disabled.
EnforcementPolicy policy = Runtime::Current()->GetCorePlatformApiEnforcementPolicy();
if (policy == EnforcementPolicy::kDisabled) {
return false;
}
// If this is a proxy method, look at the interface method instead.
member = detail::GetInterfaceMemberIfProxy(member);
// Access checks are not disabled, report the violation.
// This may also add kAccCorePlatformApi to the access flags of `member`
// so as to not warn again on next access.
return detail::HandleCorePlatformApiViolation(member,
caller_context,
access_method,
policy);
}
case Domain::kCorePlatform: {
LOG(FATAL) << "CorePlatform domain should be allowed to access all domains";
UNREACHABLE();
}
}
}
先看看决定kApplication准入域准入权限的函数ShouldDenyAccessToMemberImpl。ShouldDenyAccessToMemberImpl接收三个参数:member是要访问的方法或者域;api_list是方法或者域的的受限制情况,指明该方法或者域是否在白名单之列,如果不在白名单之列,在哪个版本的SDK可以开放等信息;access_method指的是访问该方法或者域的方式,主要分为:
1.kNone(虚拟机内部使用,上面GetDeclaredMethodInternal用的是此方式)
2.kReflection(反射调用使用)
3.kJNI = 2(JNI调用使用)
4. kLinking = 3(类链接使用)
访问政策如下:
1.如果方法或域签名在hiddenapi豁免的白名单内,例如通过adb shell settings put global hidden_api_blacklist_exemptions Lorg/apache/xpath/NodeSetDTM;->setItem(II)V的方式设置,是可以访问的;
2.对于testapi(带@test注解),在hiddenapi的限制政策开启的情况下,如果testapi限制政策禁用哪个,则可以访问这个testapi;
3.应用进程在创建虚拟机副本时,会根据自身的targetsdk传入参数到虚拟机作为虚拟机的targetsdk。在hiddenapi的限制政策开启的情况下,当虚拟机的targetsdk大于hiddenapi指定的api时,访问会被拒绝;小于等于hiddenapi指定的api时,访问被允许。
4.在hiddenapi的限制政策禁用的情况下,访问总是可以被允许。
代码注解以及其含义:
注释 | 含义 |
---|---|
@UnsupportedAppUsage | 不受限制的灰名单 |
@UnsupportedAppUsage(maxTargetSdk = 0) | 黑名单 |
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O) | 受限制的灰名单。仅供以 Android 8.1 Oreo(API 级别 27)或更低版本为目标平台的应用进行访问。 |
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) | 受限制的灰名单。仅供以 Android 9 Pie(API 级别 28)或更低版本为目标平台的应用进行访问。 |
art/runtime/hidden_api.cc
template
bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method) {
DCHECK(member != nullptr);
Runtime* runtime = Runtime::Current();
EnforcementPolicy hiddenApiPolicy = runtime->GetHiddenApiEnforcementPolicy();
DCHECK(hiddenApiPolicy != EnforcementPolicy::kDisabled)
<< "Should never enter this function when access checks are completely disabled";
MemberSignature member_signature(member);
// Check for an exemption first. Exempted APIs are treated as white list.
if (member_signature.IsExempted(runtime->GetHiddenApiExemptions())) {
// Avoid re-examining the exemption list next time.
// Note this results in no warning for the member, which seems like what one would expect.
// Exemptions effectively adds new members to the whitelist.
MaybeUpdateAccessFlags(runtime, member, kAccPublicApi);
return false;
}
EnforcementPolicy testApiPolicy = runtime->GetTestApiEnforcementPolicy();
bool deny_access = false;
if (hiddenApiPolicy == EnforcementPolicy::kEnabled) {
if (testApiPolicy == EnforcementPolicy::kDisabled && api_list.IsTestApi()) {
deny_access = false;
} else {
deny_access = IsSdkVersionSetAndMoreThan(runtime->GetTargetSdkVersion(),
api_list.GetMaxAllowedSdkVersion());
}
}
if (access_method != AccessMethod::kNone) {
// Print a log message with information about this class member access.
// We do this if we're about to deny access, or the app is debuggable.
if (kLogAllAccesses || deny_access || runtime->IsJavaDebuggable()) {
member_signature.WarnAboutAccess(access_method, api_list, deny_access);
}
// If there is a StrictMode listener, notify it about this violation.
member_signature.NotifyHiddenApiListener(access_method);
// If event log sampling is enabled, report this violation.
if (kIsTargetBuild && !kIsTargetLinux) {
uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
// Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
if (eventLogSampleRate != 0) {
const uint32_t sampled_value = static_cast(std::rand()) & 0xffff;
if (sampled_value < eventLogSampleRate) {
member_signature.LogAccessToEventLog(sampled_value, access_method, deny_access);
}
}
}
// If this access was not denied, move the member into whitelist and skip
// the warning the next time the member is accessed.
if (!deny_access) {
MaybeUpdateAccessFlags(runtime, member, kAccPublicApi);
}
}
return deny_access;
}
在java_lang_Class.cc的Class_getDeclaredMethodInternal方法中还会调用一次ShouldDenyAccessToMember,如果权限被拒绝,这次会打印出具体的警告log例如
Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI),并且令Class_getDeclaredMethodInternal返回null,导致上层的Class#getDeclaredMethod调用抛出NoSuchMethodException异常。
java_lang_Class.cc里面的ShouldDenyAccessToMember和class.cc里面GetDeclaredMethodInternal的ShouldDenyAccessToMember不同,java_lang_Class.cc是经Java层的调用,访问方式会被设置成kReflection(反射),class.cc是虚拟机内部类的实现,访问方式会被设置成kNone。
art/runtime/native/java_lang_Class.cc
// Returns true if the first non-ClassClass caller up the stack should not be
// allowed access to `member`.
template
ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
return hiddenapi::ShouldDenyAccessToMember(member,
GetHiddenapiAccessContextFunction(self),
hiddenapi::AccessMethod::kReflection);
}
在访问方式不等于kNone的情况下,还会有以下这些额外步骤:
在访问权限被拒绝(deny_access为true)或者调用者为debuggable app(runtime->IsJavaDebuggable为true)的情况下,会打印出权限拒绝的log,例如:
Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)。
在访问权限被授予的情况下,虚拟机内部还会为这个方法添加kAccPublicApi的flag,以方便下次再次调用ShouldDenyAccessToMember可以尽早以false的结果返回。
art/runtime/hidden_api.cc
template
bool ShouldDenyAccessToMemberImpl(T* member, ApiList api_list, AccessMethod access_method) {
...
if (access_method != AccessMethod::kNone) {
// Print a log message with information about this class member access.
// We do this if we're about to deny access, or the app is debuggable.
if (kLogAllAccesses || deny_access || runtime->IsJavaDebuggable()) {
member_signature.WarnAboutAccess(access_method, api_list, deny_access);
}
// If there is a StrictMode listener, notify it about this violation.
member_signature.NotifyHiddenApiListener(access_method);
// If event log sampling is enabled, report this violation.
if (kIsTargetBuild && !kIsTargetLinux) {
uint32_t eventLogSampleRate = runtime->GetHiddenApiEventLogSampleRate();
// Assert that RAND_MAX is big enough, to ensure sampling below works as expected.
static_assert(RAND_MAX >= 0xffff, "RAND_MAX too small");
if (eventLogSampleRate != 0) {
const uint32_t sampled_value = static_cast(std::rand()) & 0xffff;
if (sampled_value < eventLogSampleRate) {
member_signature.LogAccessToEventLog(sampled_value, access_method, deny_access);
}
}
}
JNI调用的情况跟反射的大同小异。入口在jni_internal.cc,也会调用ShouldDenyAccessToMember决定访问权限:
art/runtime/jni/jni_internal.cc
ArtMethod* FindMethodJNI(const ScopedObjectAccess& soa,
jclass jni_class,
const char* name,
const char* sig,
bool is_static) {
ObjPtr c = EnsureInitialized(soa.Self(), soa.Decode(jni_class));
if (c == nullptr) {
return nullptr;
}
ArtMethod* method = nullptr;
auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
if (c->IsInterface()) {
method = c->FindInterfaceMethod(name, sig, pointer_size);
} else {
method = c->FindClassMethod(name, sig, pointer_size);
}
if (method != nullptr && ShouldDenyAccessToMember(method, soa.Self())) {
method = nullptr;
}
if (method == nullptr || method->IsStatic() != is_static) {
ThrowNoSuchMethodError(soa, c, name, sig, is_static ? "static" : "non-static");
return nullptr;
}
return method;
}
虚拟机内部查找方法的过程如下:1.现在当前类的虚方法和直接方法中查找;2.遍历当前类的所有父类,在这些父类的虚方法和直接方法中查找;3.遍历当前类的所有父类,在这些父类的拷贝方法中查找。如果有返回结果,再在jni_internal.cc中通过ShouldDenyAccessToMember方法进行hiddenapi限制检验确认最终结果。
art/runtime/mirror/class.cc
template
static inline ArtMethod* FindClassMethodWithSignature(ObjPtr this_klass,
std::string_view name,
const SignatureType& signature,
PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_) {
// Search declared methods first.
for (ArtMethod& method : this_klass->GetDeclaredMethodsSlice(pointer_size)) {
ArtMethod* np_method = method.GetInterfaceMethodIfProxy(pointer_size);
if (np_method->GetName() == name && np_method->GetSignature() == signature) {
return &method;
}
}
// Then search the superclass chain. If we find an inherited method, return it.
// If we find a method that's not inherited because of access restrictions,
// try to find a method inherited from an interface in copied methods.
ObjPtr klass = this_klass->GetSuperClass();
ArtMethod* uninherited_method = nullptr;
for (; klass != nullptr; klass = klass->GetSuperClass()) {
DCHECK(!klass->IsProxyClass());
for (ArtMethod& method : klass->GetDeclaredMethodsSlice(pointer_size)) {
if (method.GetName() == name && method.GetSignature() == signature) {
if (IsInheritedMethod(this_klass, klass, method)) {
return &method;
}
uninherited_method = &method;
break;
}
}
if (uninherited_method != nullptr) {
break;
}
}
// Then search copied methods.
// If we found a method that's not inherited, stop the search in its declaring class.
ObjPtr end_klass = klass;
DCHECK_EQ(uninherited_method != nullptr, end_klass != nullptr);
klass = this_klass;
if (UNLIKELY(klass->IsProxyClass())) {
DCHECK(klass->GetCopiedMethodsSlice(pointer_size).empty());
klass = klass->GetSuperClass();
}
for (; klass != end_klass; klass = klass->GetSuperClass()) {
DCHECK(!klass->IsProxyClass());
for (ArtMethod& method : klass->GetCopiedMethodsSlice(pointer_size)) {
if (method.GetName() == name && method.GetSignature() == signature) {
return &method; // No further check needed, copied methods are inherited by definition.
}
}
}
return uninherited_method; // Return the `uninherited_method` if any.
}