Android中java类的访问权限控制在art中的Class::CanAccessMember实现。其中,参数access_to是要访问的属性或者方法所在的类,member_flags是要访问的属性或者方法的flag(Android提供public、protected、private修饰符来设置flag)。规则如下(以下规则优先级从高到低):
1.如果要访问的内容在当前类中,允许访问;
2.如果要访问的内容设置了public修饰符,允许访问;
3.如果要访问的内容设置了private修饰符,不允许访问;
4.如果要访问的内容设置了protected修饰符,当前类不是接口且是要访问的内容所在类的子类,允许访问;
5.通过Class::IsInSamePackage进行判定,该函数返回true表示允许访问。
art/runtime/mirror/class-inl.h
inline bool Class::CanAccessMember(ObjPtr<Class> access_to, uint32_t member_flags) {
// Classes can access all of their own members
if (this == access_to) {//1.
return true;
}
// Public members are trivially accessible
if (member_flags & kAccPublic) {//2.
return true;
}
// Private members are trivially not accessible
if (member_flags & kAccPrivate) {//3.
return false;
}
// Check for protected access from a sub-class, which may or may not be in the same package.
if (member_flags & kAccProtected) {//4.
if (!this->IsInterface() && this->IsSubClass(access_to)) {
return true;
}
}
// Allow protected access from other classes in the same package.
return this->IsInSamePackage(access_to);//5.
}
Class::IsInSamePackage接收一个参数that,该参数是要访问的内容所在的类。
1.当前类和参数类是同一个类时,返回true;
2.当前类和参数类绑定的不是同一个classloader时,返回false;
3.如果当前类或参数类是数组类型,则将它们的基本类型类进行比较(例如java.lang.String[]的基本类型类是java.lang.String),如果它们的基本类型类是同一个类,返回true。
4.如果当前类和参数类在同一个包里,返回true。判断同一个包的规则是找到它们在art里面的限定类名(例如Ljava/lang/invoke/MethodHandle和Ljava/lang/invoke/CallSite),从头开始找到它们的最大相同子串的下标(最大子串为Ljava/lang/invoke/),如果两个类从该下标开始都没有出现’/’,可以视作是在同一个包(java/lang/invoke/后面分别是MethodHandle和CallSite,没有出现’/’,所以是在同一个包)。
art/runtime/mirror/class.cc
bool Class::IsInSamePackage(ObjPtr<Class> that) {
ObjPtr<Class> klass1 = this;
ObjPtr<Class> klass2 = that;
if (klass1 == klass2) {//1.
return true;
}
// Class loaders must match.
if (klass1->GetClassLoader() != klass2->GetClassLoader()) {//2.
return false;
}
// Arrays are in the same package when their element classes are.
while (klass1->IsArrayClass()) {
klass1 = klass1->GetComponentType();
}
while (klass2->IsArrayClass()) {
klass2 = klass2->GetComponentType();
}
// trivial check again for array types
if (klass1 == klass2) {//3.
return true;
}
// Compare the package part of the descriptor string.
std::string temp1, temp2;
return IsInSamePackage(klass1->GetDescriptor(&temp1), klass2->GetDescriptor(&temp2));//4.
}
art/runtime/mirror/class.cc
bool Class::IsInSamePackage(std::string_view descriptor1, std::string_view descriptor2) {
size_t i = 0;
size_t min_length = std::min(descriptor1.size(), descriptor2.size());
while (i < min_length && descriptor1[i] == descriptor2[i]) {
++i;
}
if (descriptor1.find('/', i) != std::string_view::npos ||
descriptor2.find('/', i) != std::string_view::npos) {
return false;
} else {
return true;
}
}