前言
上一篇文章我使用了 Shizuku 去调用系统API:文章链接
这次就来看看 Shizuku 的源码是怎么写的。
最开始我用 Notepad++ 看,最后还是用 as 看源码好点吧。
Shizuku 是什么?
Shizuku app 会引导用户使用 root 或是 adb 方式运行一个进程(Shizuku 服务进程)。
- 应用进程启动时 Shizuku 服务进程发送 binder 至应用进程
- 应用通过该 binder 与 Shizuku 服务进程交互,Shizuku 服务进程通过 binder 与 system server 交互
正文
ShizukuBinderWrapper
首先我们看,在开发时调用 Shizuku 的代码:
private static final IPackageManager PACKAGE_MANAGER = IPackageManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")));
它使我们能随意调用 Android 系统中 IPackageManager 内的方法,也就是系统隐藏的API(有 @hide 标签的方法)
我们可以看到 ShizukuBinderWrapper 的构造方法内传入了一个 SystemServiceHelper#getSystemService 方法的返回值。
private static Map<String, IBinder> systemServiceCache = new HashMap<>();
public static IBinder getSystemService(@NonNull String name) {
IBinder binder = systemServiceCache.get(name);
if (binder == null) {
binder = ServiceManager.getService(name);
systemServiceCache.put(name, binder);
}
return binder;
}
getSystemService 方法先查询了一下缓存,如果没有缓存就调用 ServiceManager 的 getService 方法。这个本地的 ServiceManager 是在 android.os 包下:
package android.os;
public class ServiceManager {
public static IBinder getService(String name) {
throw new UnsupportedOperationException();
}
}
getService 方法会根据 name 参数返回一个 IBinder 接口,我们可以查一下 Android 源码中的 ServiceManager 类:
public final class ServiceManager {
/**
* Returns a reference to a service with the given name.
*
* @param name the name of the service to get
* @return a reference to the service, or <code>null</code> if the service doesn't exist
*/
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(rawGetService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
}
其中 getService 不是 hide 方法可以直接调用。
我们看看 ShizukuBinderWrapper 的构造参数:
private IBinder original;
// original 是 ServiceManager.getService 返回的 IBinder
public ShizukuBinderWrapper(@NonNull IBinder original) {
// 使用 requireNonNull 方法判断是否为 null,如果为 null 抛出空指针异常。
this.original = Objects.requireNonNull(original);
}
这个构造参数对 original 进行了赋值,这是一个 IBinder。
我们将这个 ShizukuBinderWrapper 传入Binder 的 asInterface 就能获得适用于客户端的 Binder 随意调用 IPackageiManager 内的方法,其中我在本地写的 IPackageManager 只是个空壳接口,只继承了 IInterface 声明了需要的方法:
package android.content.pm;
// 与 Android 系统内的 IPackageManager 在同一个包下
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
public interface IPackageManager extends IInterface {
// 我只需要这个方法,所以只声明这个方法来调用
ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) throws RemoteException;
abstract class Stub extends Binder implements IPackageManager {
public static IPackageManager asInterface(IBinder obj) {
throw new UnsupportedOperationException();
}
}
}
我们查看 ShizukuBinderWrapper 的源码, ShizukuBinderWrapper 本身就实现了 IBinder 接口,所以可以直接作为上述 asInterface 方法的参数。
这是 ShizukuBinderWrapper 中的 transact 方法的重写:
/* ShizukuBinderWrapper#transact */
@Override
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
Parcel newData = Parcel.obtain();
try {
// 使用 writeInterfaceToken 写入 ShizukuApiConstants.BINDER_DESCRIPTOR,用于后面服务端的判断
newData.writeInterfaceToken(ShizukuApiConstants.BINDER_DESCRIPTOR);
// 写入了 original 这个 Binder
newData.writeStrongBinder(original);
newData.writeInt(code);
newData.appendFrom(data, 0, data.dataSize());
ShizukuService.transactRemote(newData, reply, flags);
} finally {
newData.recycle();
}
return true;
}
我们都知道,在 aidl 中我们调用 Binder 的方法时实际上都调用了这个 transact 方法。
例如:
private static final IPackageManager PACKAGE_MANAGER = IPackageManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")));
ParceledListSlice<PackageInfo> listSlice = PACKAGE_MANAGER.getInstalledPackages(flags, userId);
这里我调用 getInstalledPackages 方法时,方法的内部实际上就调用了上面 ShizukuBinderWrapper 这个 Binder 的 transact 方法。在调用 getInstalledPackages 这个系统方法时,它内部就把我们调用系统方法所传入的参数写入了 data 中再传入这个 ShizukuBinderWrapper 的 transact 方法。
ShizukuBinderWrapper 的 transact 方法又把 data 和 reply 传入了 ShizukuService 的 transactRemote 下,这个 ShizukuService 是一个普通的类里面有一些静态变量和方法。
package moe.shizuku.api;
/* ShizukuService#transactRemote */
public static void transactRemote(@NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
// 调用 ShizukuService 的 requireService 方法
requireService().asBinder().transact(ShizukuApiConstants.BINDER_TRANSACTION_transact, data, reply, flags);
}
我们可以看到,这个方法由调用了 requireService 方法取得了 Binder 再调用 Binder 的 transact 方法与服务端通信 ,再看看 requireService 方法:
private static IShizukuService requireService() {
if (getService() == null) {
throw new IllegalStateException("Binder haven't received, check Shizuku and your code.");
}
return getService();
}
没什么好解释,继续看 getService 方法。
/* ShizukuService */
private static IShizukuService sService;
public static void setBinder(IBinder binder) {
sService = IShizukuService.Stub.asInterface(binder);
}
private static IShizukuService getService() {
return sService;
}
这个 getService 静态方法会返回一个 IShizukuService 类型的 mService,而这个 mService 是由 setBinder 设置的,那么是谁调用了这个 ShizukuService.setBinder 方法来设置 mService ?我们先不管它。
IShizukuService
我们再注意一下这个 IShizukuService:
/* IShizukuService.aidl */
package moe.shizuku.server;
import moe.shizuku.server.IRemoteProcess;
interface IShizukuService {
int getVersion() = 2;
int getUid() = 3;
int checkPermission(String permission) = 4;
String getToken() = 5;
boolean setPidToken(in String token) = 6;
IRemoteProcess newProcess(in String[] cmd, in String[] env, in String dir) = 7;
String getSELinuxContext() = 8;
}
这个 AIDL 的作用是让我们开发的应用与 Shhizuku 的系统进程进行 Binder 通信。
我们之前的 transactRemote 方法已经调用了它的 Binder 的 transact 方法开始联系服务端挂起客户端了。
我们再看与 IShizukuService 相关的类:
package moe.shizuku.service
public class ShizukuService extends IShizukuService.Stub {
...
}
它不同于我们刚才看到的 moe.shizuku.api 下的 ShizukuService ,这是 moe.shizuku.service 下的 ShizukuService。是一个真真正正的服务端,和 Android 系统中的系统服务(例如AMS, PMS)类似没有直接继承 Service 类,而是继承自一个 Binder(后面我们再说)。
接下来我们看它重写的 onTransact 方法:
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
if (code == ShizukuApiConstants.BINDER_TRANSACTION_transact) {
data.enforceInterface(ShizukuApiConstants.BINDER_DESCRIPTOR);
transactRemote(data, reply, flags);
return true;
}
return super.onTransact(code, data, reply, flags);
}
先用 enforceInterface 方法检查一下 Interface 是否与客户端的相同,再调用了 transactRemote 方法:
private void transactRemote(Parcel data, Parcel reply, int flags) throws RemoteException {
IBinder targetBinder = data.readStrongBinder();
int targetCode = data.readInt();
enforceCallingPermission("transactRemote", true);
targetBinder.getInterfaceDescriptor(), targetCode);
Parcel newData = Parcel.obtain();
try {
newData.appendFrom(data, data.dataPosition(), data.dataAvail());
} catch (Throwable tr) {
LOGGER.w(tr, "appendFrom");
return;
}
try {
long id = Binder.clearCallingIdentity();
// here
targetBinder.transact(targetCode, newData, reply, flags);
Binder.restoreCallingIdentity(id);
} finally {
newData.recycle();
}
}
我们可以看到 targetBinder 其实就是我们之前用 SystemServiceHelper 的 getService 方法得到的 Binder,在调用它的 transact 方法,实现 Shizuku 服务与你想要通信的系统服务进行 Binder 通信,毕竟 Shizuku 服务已经通过 adb 或 root 成为了 dalao。
ShizukuService 的启动
上面我们看到了我们用到了 ShizukuService 去完成应用与系统服务的通信,而这个静态变量 mService 是通过 ShizukuService#setBinder 方法设置的:
package moe.shizuku.api;
public class ShizukuService {
private static IShizukuService sService;
public static void setBinder(IBinder binder) {
sService = IShizukuService.Stub.asInterface(binder);
}
}
首先,Starter 类中的 main 方法会启动 ShizukuService 服务:
public static void main(String[] args) throws IOException, RemoteException, InterruptedException {
fixFilesOwner();
waitServiceManager();
waitSystemService("package");
waitSystemService("activity");
waitSystemService(Context.USER_SERVICE);
waitSystemService(Context.APP_OPS_SERVICE);
checkManagerApp();
if (Build.VERSION.SDK_INT >= 28) {
disableHiddenApiBlacklist();
}
LOGGER.i("server v3");
Looper.prepare();
// 这是 moe.shizuku.service 包下的继承自 Binder 的 ShizukuService
ShizukuService server = new ShizukuService(getToken(args));
server.sendBinderToManager();
server.sendBinderToClients();
Looper.loop();
LOGGER.i("server exit");
System.exit(0);
}
可以看到这里直接 new 了一个 ShizukuService 实例,这和一些系统服务启动方法类似。
其中调用了 ShizukuService 的 sendBinderToClients 方法,这中间有一大堆 dalao 操作,我们不作详细关注,泥萌可自行阅读源码。
最后会调用 ShuzikuService 的 sendBinderToUserApp 方法,略过了一些其他的代码:
static void sendBinderToUserApp(Binder binder, String packageName, int userId) {
...
Bundle extra = new Bundle();
extra.putParcelable(ShizukuApiConstants.EXTRA_BINDER, new BinderContainer(binder));
Bundle reply = IContentProviderHelper.call(provider, null, name, "sendBinder", null, extra);
...
}
将 ShizukuService 这个 Binder 包装成一个 BinderContainer 类然后放入 Bundle 里。
这个 IContentProviderHelper 的 call 方法会跨进程调用 ShizukuBinderReceiveProvider 这个 ContentProvider 的 call 方法:
@Nullable
@Override
public final Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
if (extras == null)
return null;
Bundle reply = new Bundle();
extras.setClassLoader(BinderContainer.class.getClassLoader());
switch (method) {
case METHOD_SEND_BINDER: {
if (ShizukuService.pingBinder()) {
Log.i("ShizukuClient", "ShizukuBinderReceiveProvider started when already a binder alive");
break;
}
BinderContainer container = extras.getParcelable(ShizukuApiConstants.EXTRA_BINDER);
if (container != null && container.binder != null) {
Log.i("ShizukuClient", "binder received");
// 调用 ShizukuService 的 setBinder 静态方法将静态变量 mService 设置成之前在 app_process 进程启动的 ShizukuService 的 Binder
ShizukuService.setBinder(container.binder);
//noinspection ConstantConditions
Intent intent = new Intent(ShizukuMultiProcessHelper.ACTION_BINDER_RECEIVED)
.putExtra(ShizukuApiConstants.EXTRA_BINDER, container)
.setPackage(getContext().getPackageName());
getContext().sendBroadcast(intent);
}
...
break;
}
...
}
return reply;
}
其中调用了 ShizukuService 的 setBinder 方法,将 IShuzukuService 传进去了。
末语
实际上这只分析了一部分,关于 ShizukuService 具体的如何启动,这个 main 方法是怎么调用的,和关于 start.sh 的一些问题,或许有生之年我能水写出第二篇文章…
Shizuku 内还有好多 dalao操作,我都没有去看,只把重要的一部分写了下来。
果然只有 dalao才能这样操作,我还是继续躺着吧。
Orz
2020-02-04:
时隔两天,我又写出了第二篇文章啦:https://lcblog.cn/post/android-shizuku-theory2
尝试具体分析 Starter 类是如何启动的。