1.背景
客户反馈,普通应用做platform签名后可以创建用户,影响系统安全。
2.发现问题
自测试代码:
public void createUser() {
try {
UserManager mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
mUserManager.createUser("test", 0);
} catch (Exception e) {
e.printStackTrace();
} catch (NoClassDefFoundError e) {
e.printStackTrace();
}
}
声明相关权限:
<uses-permission android:name="android.permission.MANAGE_USERS"/>
<uses-permission android:name="android.permission.CREATE_USERS"/>
编写代码时发现,eclipse根本调用不到createUser这个方法…
这是为什么呢?可能是系统隐藏了这个方法。
frameworks/base/core/java/android/os/UserManager.java
/**
* Creates a user with the specified name and options. For non-admin users, default user
* restrictions are going to be applied.
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
*
* @param name the user's name
* @param flags flags that identify the type of user and other properties.
* @see UserInfo
*
* @return the UserInfo object for the created user, or null if the user could not be created.
* @hide
*/
public UserInfo createUser(String name, int flags) {
UserInfo user = null;
try {
user = mService.createUser(name, flags);
// TODO: Keep this in sync with
// UserManagerService.LocalService.createUserEvenWhenDisallowed
if (user != null && !user.isAdmin() && !user.isDemo()) {
mService.setUserRestriction(DISALLOW_SMS, true, user.id);
mService.setUserRestriction(DISALLOW_OUTGOING_CALLS, true, user.id);
}
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
return user;
}
果然,Android源码里面果然声明了@hide,那这个现象就是正常的了。那么,怎样才能调用createUser方法呢?
3.解决问题
3.1 反射调用
直接调用不行,那就尝试反射吧。
public void createUser() {
try {
UserManager mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
//mUserManager.createUser("test", 0);
// Class<?> cls = Class.forName("android.os.UserManager");
Class<?> cls = UserManager.class;
Method createUser = cls.getMethod("createUser", String.class, int.class);
Log.d(TAG, "createUser = " + createUser.getName());
Object objRet1 = createUser.invoke(mUserManager, "test", 0);
Log.d(TAG, "ret = " + objRet1);
} catch (Exception e) {
e.printStackTrace();
} catch (NoClassDefFoundError e) {
e.printStackTrace();
}
}
经验证,反射调用的这段代码是可以的。
3.2 引入layoutlib.jar
客户反馈不需要反射,应用可以直接调用这个方法。为什么我调用不了呢?继续分析,猜测跟layoutlib.jar有关,尝试eclipse工程引入layoutlib.jar。
如何引用layoutlib.jar就不多介绍了,很简单。如上图,引用layoutlib.jar。发现还是调用不了。究竟是什么原因呢?
import android.os.UserManager;
跟踪UserManager,发现这个类还是定义在android.jar里面,layoutlib.jar没有生效。
可能跟jar包的顺序有关,调整一下优先级,把layoutlib.jar调到第一位。
再看一下引用关系。
如上图,现在UserManager已经是layoutlib.jar里面的了,说明引用正确了。
现在确认一下能否调用createUser方法。
eclipse没有报错了,说明layoutlib.jar里面确实有UserManager的createUser方法。
4.为什么需要platform签名
看一下权限是如何定义的。
frameworks/base/core/res/AndroidManifest.xml
<!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
users on the device. This permission is not available to
third party applications. -->
<permission android:name="android.permission.MANAGE_USERS"
android:protectionLevel="signature|privileged" />
<!-- @hide Allows an application to create, remove users and get the list of
users on the device. Applications holding this permission can only create restricted,
guest, managed, demo, and ephemeral users. For creating other kind of users,
{@link android.Manifest.permission#MANAGE_USERS} is needed.
This permission is not available to third party applications. -->
<permission android:name="android.permission.CREATE_USERS"
android:protectionLevel="signature" />
signature级别的权限,需要申请者和定义者具有相同的签名。
这两个权限是在framework-res.jar里面定义的,而framework-res.jar是platform签名,所以申请者也需要platform签名,才能得到这两个权限。
关于Android权限管理机制,参考这篇文章:
https://blog.csdn.net/xhaotianshenjian/article/details/81535259