1 系统AIDL服务与ServiceManger
1.1 启动
- ServiceManager是Binder IPC通信过程中的守护进程,本身也是一个Binder服务,用来管理系统Service组件。主要用来查询和注册服务。并且向Client组件提供获取Service代理对象的服务。
- ServiceManager运行在一个独立的进程中,Service组件与Client组件需要Binder进程间通信机制对其访问。
- ServiceManger由init进程负责启动。
1.2 注册与查询
- 由于SM的句柄值为0,获取SM代理对象省去了与Binder驱动的交互,进程可直接根据句柄创建SM代理对象。
- SystemServer进程启动AMS时,调用addService将AMS注册到ServiceManger。
- App进程想要访问AMS,需要调用getService向ServiceManger获取AMS的代理对象。
- 最后App进程通过AMS的代理对象访问AMS。
2 VA中的VAMS、VPMS等AIDL服务
VA实现对内部AIDL Service的管理,其设计思想与系统基本一致。
- ServiceFetcher是一个Binder对象,VA中的作用类似系统的ServiceManger,用来缓存VAMS、VPMS等AIDL Service的Binder本地对象。
- ServiceFetcher与VActivityMangerService的Binder本地对象都存活在VA:X进程,addService过程不再跨进程,直接缓存。
- Android系统获取SM代理对象的过程,改为了使用ContentProvider.call(),App进程可以从返回值Bundle中获取ServiceFetcher代理对象。
- App进程调用ServiceFetcher.getService()获取VAMS的代理对象,才能跨进程访问VAMS。
2.1 启动、注册服务
public final class BinderProvider extends ContentProvider {
private final ServiceFetcher mServiceFetcher = new ServiceFetcher();
...
private boolean init() {
...
addService(ServiceManagerNative.ACTIVITY, VActivityManagerService.get());
addService(ServiceManagerNative.USER, VUserManagerService.get());
...
return true;
}
private void addService(String name, IBinder service) {
ServiceCache.addService(name, service);
}
@Override
public Bundle call(String method, String arg, Bundle extras) {
if (!sInitialized) {
init();
}
if ("@".equals(method)) {
Bundle bundle = new Bundle();
BundleCompat.putBinder(bundle, "_VA_|_binder_", mServiceFetcher);
return bundle;
}
return null;
}
private class ServiceFetcher extends IServiceFetcher.Stub {
@Override
public IBinder getService(String name) throws RemoteException {
if (name != null) {
return ServiceCache.getService(name);
}
return null;
}
@Override
public void addService(String name, IBinder service) throws RemoteException {
if (name != null && service != null) {
ServiceCache.addService(name, service);
}
}
@Override
public void removeService(String name) throws RemoteException {
if (name != null) {
ServiceCache.removeService(name);
}
}
}
}
2.2 获取VAMS代理对象
public class VActivityManager {
private static final VActivityManager sAM = new VActivityManager();
private IActivityManager mService;
public IActivityManager getService() {
if (!IInterfaceUtils.isAlive(mService)) {
synchronized (VActivityManager.class) {
final Object remote = getRemoteInterface();
mService = LocalProxyUtils.genProxy(IActivityManager.class, remote);
}
}
return mService;
}
private Object getRemoteInterface() {
return IActivityManager.Stub
.asInterface(ServiceManagerNative.getService(ServiceManagerNative.ACTIVITY));
}
}
public class ServiceManagerNative {
public static final String SERVICE_DEF_AUTH = "virtual.service.BinderProvider";
private static final String TAG = ServiceManagerNative.class.getSimpleName();
public static String SERVICE_CP_AUTH = "virtual.service.BinderProvider";
private static IServiceFetcher sFetcher;
private static String getAuthority() {
return VirtualCore.getConfig().getBinderProviderAuthority();
}
private static IServiceFetcher getServiceFetcher() {
if (sFetcher == null || !sFetcher.asBinder().isBinderAlive()) {
synchronized (ServiceManagerNative.class) {
Context context = VirtualCore.get().getContext();
Bundle response = new ProviderCall.Builder(context, getAuthority()).methodName("@").callSafely();
if (response != null) {
IBinder binder = BundleCompat.getBinder(response, "_VA_|_binder_");
linkBinderDied(binder);
sFetcher = IServiceFetcher.Stub.asInterface(binder);
}
}
}
return sFetcher;
}
public static IBinder getService(String name) {
if (VirtualCore.get().isServerProcess()) {
return ServiceCache.getService(name);
}
IServiceFetcher fetcher = getServiceFetcher();
if (fetcher != null) {
try {
return fetcher.getService(name);
} catch (RemoteException e) {
e.printStackTrace();
}
}
VLog.e(TAG, "GetService(%s) return null.", name);
return null;
}
}
3 Service的生命周期
-
onCreate()
首次创建服务时,系统将调用此方法。如果服务已在运行,则不会调用此方法,该方法只调用一次。 -
onStartCommand()
当另一个组件通过调用startService()请求启动服务时,系统将调用此方法。 -
onDestroy()
当服务不再使用且将被销毁时,系统将调用此方法。 -
onBind()
当另一个组件通过调用bindService()与服务绑定时,系统将调用此方法。 -
onUnbind()
当另一个组件通过调用unbindService()与服务解绑时,系统将调用此方法。 -
onRebind()
当旧的组件与服务解绑后,另一个新的组件与服务绑定,onUnbind()返回true时,系统将调用此方法。
A:onCreate()
B:onStartCommand()
C:onDestroy()
D:onBind()
E:onUnbind()
F:onRebind()
顺序 | 结果 |
---|---|
startService->stopService | A-B-C |
bindService->unbindService | A-D-E-C |
startService->startService | A-B-B |
startService->bindService->stopService | A-B-D |
startService->bindService->unbindService | A-B-D-E |
bindService->startService->unbindService | A-D-B-E |
startService->bindService->unbindService->bindService | A-B-D-E(true)-F |
4 VA拦截startService
4.1 startService的流程
Service的生命周期比较简单,没有界面和交互,跟其他Framework Service没有任何关系,所以不需要告知AMS,生命周期部分绕过AMS完全由VA控制,最后直接调用ApplicationThread(VClient)处理,这跟Activity占坑的实现方式不同。VA尝试使用过Service占坑的方法,由AMS启动占坑的Service,再由占坑Service去控制真正Service的生命周期,这种方法会经过系统AMS控制,生命周期如果没有正常返回会出现ANR,例如VA中运行微信7.0版本偶现ANR。
4.1.1 Service的ANR
- 简单来说service调用onCreate或者onStartCommand之前会延迟发送一个timeout消息,延时为20s或者200s,然后执行完了之后会移除掉该timeout消息,如果不能及时移除,则处理timeout消息,导致service anr。
- 所以Service的生命周期中不能做耗时操作,生命周期必须正常返回,才能在serviceDoneExecutingLocked中移除timeout消息。
- 参考https://blog.csdn.net/sinat_20059415/article/details/80997425
4.2 startService相关的代码
4.2.1 MethodProxies$StartService
- VA:X进程也Hook了AM,该进程调用startService,直接跳过走系统。
- 如果VA内查不到该要启动服务,判断isVisiblePackage,会去VA外应用白名单中查找是否有该服务。
static class StartService extends MethodProxy {
@Override
public Object call(Object who, Method method, Object... args) throws Throwable {
...
if (service.getComponent() != null
&& getHostPkg().equals(service.getComponent().getPackageName())) {
return method.invoke(who, args);
}
...
ServiceInfo serviceInfo = VirtualCore.get().resolveServiceInfo(service, VUserHandle.myUserId());
if (serviceInfo != null) {
return VActivityManager.get().startService(service, resolvedType, userId);
}
ResolveInfo resolveInfo = VirtualCore.get().getUnHookPackageManager().resolveService(service, 0);
if (resolveInfo == null || !isVisiblePackage(resolveInfo.serviceInfo.applicationInfo)) {
return null;
}
return method.invoke(who, args);
}
4.2.2 VAMS.startServiceCommonLocked
- r.startId用来记录调用startService的个数,如果unbindService时判断startId=0,才会stopService。
- scheduleCreateService只被调用一次,scheduleServiceArgs每次startService都被调用。
- 当启动Service时,调用startShadowService用来提高进程优先级,这点单独介绍。
private ComponentName startServiceCommonLocked(Intent service,
boolean scheduleServiceArgs, int userId) {
ServiceInfo serviceInfo = VirtualCore.get().resolveServiceInfo(service, userId);
if (serviceInfo == null) {
return null;
}
ProcessRecord targetApp = startProcessIfNeedLocked(ComponentUtils.getProcessName(serviceInfo),
userId,
serviceInfo.packageName, -1, VBinder.getCallingUid());
if (targetApp == null) {
VLog.e(TAG, "Unable to start new process (" + ComponentUtils.toComponentName(serviceInfo) + ").");
return null;
}
ServiceRecord r;
synchronized (mHistory) {
r = findRecordLocked(userId, serviceInfo);
}
if (r == null) {
r = new ServiceRecord();
r.startId = 0;
r.activeSince = SystemClock.elapsedRealtime();
r.process = targetApp;
r.serviceInfo = serviceInfo;
try {
targetApp.client.scheduleCreateService(r, r.serviceInfo);
} catch (RemoteException e) {
e.printStackTrace();
}
startShadowService(targetApp);
addRecord(r);
}
r.lastActivityTime = SystemClock.uptimeMillis();
if (scheduleServiceArgs) {
r.startId++;
try {
targetApp.client.scheduleServiceArgs(r, r.startId, service);
} catch (RemoteException e) {
e.printStackTrace();
}
}
return ComponentUtils.toComponentName(serviceInfo);
}
4.2.3 VClient.handleCreateService
- 如果是startService启动的进程,该进程还没有跟app绑定,调用bindApplication完成绑定
- 从LoadedApk中拿到应用的ClassLoader来创建Service的实例
- 创建一个ContextImpl对象,这个context是VA的context,调用service.attach将ContextImpl设置到ContextWrapper的mBaseContext变量中,这样service就有能力调用context的接口。
- VA的context.mPackageManager是没有被Hook的,调用fixContext先将ContextImpl.mPackageManager置空,然后调用getPackageManager重新设置被Hook的PM。
private void handleCreateService(CreateServiceData data) {
ServiceInfo info = data.info;
if (!isAppRunning()) {
bindApplication(info.packageName, info.processName);
}
ClassLoader classLoader = LoadedApk.getClassLoader.call(mBoundApplication.info);
Service service;
try {
service = (Service) classLoader.loadClass(info.name).newInstance();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate service " + info.name
+ ": " + e.toString(), e);
}
try {
Context context = VirtualCore.get().getContext().createPackageContext(
data.info.packageName,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY
);
ContextImpl.setOuterContext.call(context, service);
mirror.android.app.Service.attach.call(
service,
context,
VirtualCore.mainThread(),
info.name,
clientConfig.token,
mInitialApplication,
ActivityManagerNative.getDefault.call()
);
ContextFixer.fixContext(service);
service.onCreate();
mServices.put(data.token, service);
VActivityManager.get().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (Exception e) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
4.2.4 VA使用ShadowService提高进程优先级
- VA的AndroidManifest.xml中定义了100个ShadowService,每个进程对应一个ShadowService。
- 如果一个进程以startService的方法启动,那么这个进程的优先级属于服务进程,进程的优先级:前台进程>可见进程>服务进程>后台进程
- Service的优先级被默认后台任务,如果bindService设置了BIND_AUTO_CREATE则Service的优先级将等同于宿主进程,也就是调用bindService的进程。
- 也就是说VA:X进程去调用bindService,绑定了启动进程的ShadowService,进程优先级提高到了与VA:X进程(前台进程)相同。
- 例如在VA中打开wps应用,wps调用startService启动了cn.wps.moffice_eng:pushservice和cn.wps.moffice_eng:scan进程,如果不调用startShadowService,使用cat /proc/pid/oom_adj看到这两个进程的优先级都是11,如果调用startShadowService,这两个进程的优先级都是0,跟VA:X进程一致。
private void startShadowService(ProcessRecord app) {
String serviceName = StubManifest.getStubServiceName(app.vpid);
Intent intent = new Intent();
intent.setClassName(StubManifest.getStubPackageName(app.is64bit), serviceName);
try {
VirtualCore.get().getContext().bindService(intent, app.conn, Context.BIND_AUTO_CREATE);
} catch (Exception e) {
e.printStackTrace();
}
}
5 VA拦截bindService
5.1 举例说明bindService
- bindService()目的是回调onBind方法,作用是在Service和调用者之间建立一个桥梁,
-
ServiceConnection是一个interface,会将ServiceConnection封装成一个实现了IServiceConnection接口的Binder本地对象InnerConnection,调用bindService将InnerConnection的代理对象传给AMS,最后AMS会用InnerConnection给App进程传递LocalService的代理对象。
public class BindingActivity extends Activity {
LocalService mService;
protected void onCreate(Bundle savedInstanceState) {
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
}
};
}
public class LocalService extends Service {
private final IBinder mBinder = new LocalBinder();
private final Random mGenerator = new Random();
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
public IBinder onBind(Intent intent) {
return mBinder;
}
}
5.2 VA中bindService的流程
- VA中bindService的流程与系统相似,只不过给ServiceConnect多封装了一层代理ServiceConnectDelegate,给onBind返回的Service的代理对象也封装了一层代理BinderDelegateService。
5.2.1 ServiceConnectionDelegate
- 每个进程可能会有多个组件想要绑定服务,DELEGATE_MAP用来缓存多个IServiceConnection本地对象与ServiceDispatcher(LoadedApk.getServiceDispatcher的返回值)的对应关系
- 每一个绑定过Service组件的Activity组件在LoadedApk中都有一个对应的ServiceDispatcher对象,它负责将这个被绑定的service组件与绑定它的Actvity组件关联起来,
public class ServiceConnectionDelegate extends IServiceConnection.Stub {
private final static ArrayMap DELEGATE_MAP = new ArrayMap<>();
private IServiceConnection mConn;
private ServiceConnectionDelegate(IServiceConnection mConn) {
this.mConn = mConn;
}
public static IServiceConnection getDelegate(Context context, ServiceConnection connection, int flags) {
IServiceConnection sd = null;
if (connection == null) {
throw new IllegalArgumentException("connection is null");
}
try {
Object activityThread = ActivityThread.currentActivityThread.call();
Object loadApk = ContextImpl.mPackageInfo.get(VirtualCore.get().getContext());
Handler handler = ActivityThread.getHandler.call(activityThread);
sd = LoadedApk.getServiceDispatcher.call(loadApk, connection, context, handler, flags);
} catch (Exception e) {
Log.e("ConnectionDelegate", "getServiceDispatcher", e);
}
if (sd == null) {
throw new RuntimeException("Not supported in system context");
}
return getDelegate(sd);
}
public static ServiceConnectionDelegate getDelegate(IServiceConnection conn) {
if (conn instanceof ServiceConnectionDelegate) {
return (ServiceConnectionDelegate) conn;
}
IBinder binder = conn.asBinder();
ServiceConnectionDelegate delegate = DELEGATE_MAP.get(binder);
if (delegate == null) {
delegate = new ServiceConnectionDelegate(conn);
DELEGATE_MAP.put(binder, delegate);
}
return delegate;
}
@Override
public void connected(ComponentName name, IBinder service) throws RemoteException {
connected(name, service, false);
}
public void connected(ComponentName name, IBinder service, boolean dead) throws RemoteException {
IBinderDelegateService delegateService = IBinderDelegateService.Stub.asInterface(service);
if (delegateService != null) {
name = delegateService.getComponent();
service = delegateService.getService();
IBinder proxy = ProxyServiceFactory.getProxyService(VClient.get().getCurrentApplication(), name, service);
if (proxy != null) {
service = proxy;
}
}
if (BuildCompat.isOreo()) {
IServiceConnectionO.connected.call(mConn, name, service, dead);
} else {
mConn.connected(name, service);
}
}
}
5.2.2 ServiceRecord和IntentBindRecord
- ServiceRecord.IntentBindRecord用来描述跟这个Service绑定的应用进程,而且以Filter为关键字,保存在ServiceRecord.bindings中。
- 由于一个Service组件可以被同个应用进程的多个Activity组件绑定,这些IServiceConnection都被放到IntentBindRecord.connections列表中。
- doRebind,Service的onUnbind()的返回值为true时,将通过AMS的unbindFinished()传给AMS,记录在doRebind标志位中。下一次再有client绑定服务时,将根据doRebind的值来决定是调用onBind(),还是调用onRebind()。
public class ServiceRecord extends Binder {
final List bindings = new ArrayList<>();
long activeSince;
long lastActivityTime;
ServiceInfo serviceInfo;
int startId; //记录了调用startService的次数
ProcessRecord process;
int foregroundId;
Notification foregroundNoti;
public boolean containConnection(IServiceConnection connection) {
synchronized (bindings) {
for (IntentBindRecord record : bindings) {
if (record.containConnectionLocked(connection)) {
return true;
}
}
}
return false;
}
IntentBindRecord peekBindingLocked(Intent service) {
for (IntentBindRecord bindRecord : bindings) {
if (bindRecord.intent.filterEquals(service)) {
return bindRecord;
}
}
return null;
}
void addToBoundIntentLocked(Intent intent, IServiceConnection connection) {
IntentBindRecord record = peekBindingLocked(intent);
if (record == null) {
record = new IntentBindRecord();
record.intent = intent;
bindings.add(record);
}
record.addConnectionLocked(connection);
}
}
static class IntentBindRecord {
final List connections = new ArrayList<>();
IBinder binder;
Intent intent; //service intent
boolean doRebind = false;
...
}
}
5.2.3 VAMS.bindService
- 首先向VPMS中差找到ServiceInfo,在查找ServiceRecord,如果不存在,调用startServiceCommonLocked先启动Service,并且生命周期只调用onCreate,不调用onStartCommand。
- ServiceRecord中记录有ProcessRecord,调用scheduleBindService到应用进程。
- VA拦截了UnbindFinished(MethodProxies),boundRecord.doRebind的值在这里被赋值。
- 如果boundRecord非空,说明Service已经被绑定过,不再执行onBind()生命周期,直接调用connectServiceLocked去连接。
- addToBoundIntentLocked保存IntentBindRecord和IServiceConnection。
public int bindService(IBinder caller, IBinder token, Intent service, String resolvedType,
IServiceConnection connection, int flags, int userId) {
synchronized (this) {
ServiceInfo serviceInfo = VirtualCore.get().resolveServiceInfo(service, userId);
if (serviceInfo == null) {
return 0;
}
ServiceRecord r;
synchronized (mHistory) {
r = findRecordLocked(userId, serviceInfo);
}
boolean firstLaunch = r == null;
if (firstLaunch) {
if ((flags & Context.BIND_AUTO_CREATE) != 0) {
startServiceCommonLocked(service, false, userId);
synchronized (mHistory) {
r = findRecordLocked(userId, serviceInfo);
}
}
}
if (r == null) {
return 0;
}
synchronized (r.bindings) {
ServiceRecord.IntentBindRecord boundRecord = r.peekBindingLocked(service);
if (boundRecord != null && boundRecord.binder != null && boundRecord.binder.isBinderAlive()) {
if (boundRecord.doRebind) {
try {
r.process.client.scheduleBindService(r, service, true);
} catch (RemoteException e) {
e.printStackTrace();
}
}
ComponentName componentName = new ComponentName(r.serviceInfo.packageName, r.serviceInfo.name);
connectServiceLocked(connection, componentName, boundRecord, false);
} else {
try {
r.process.client.scheduleBindService(r, service, false);
} catch (RemoteException e) {
e.printStackTrace();
}
}
r.lastActivityTime = SystemClock.uptimeMillis();
r.addToBoundIntentLocked(service, connection);
}
return 1;
}
}
5.2.4 VClient.handleBindService
- 根据rebind的值判断调用onBind或者onRebind
- serviceDoneExecuting更新VAMS中缓存的ServiceRecord
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
VActivityManager.get().publishService(data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
VActivityManager.get().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
} catch (Exception e) {
throw new RuntimeException(
"Unable to bind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
6 VA:X进程的保活
- VA在启动BinderProvider时,启动的KeepAliveService服务,虽然VA:X进程Hook了AM,但startService过滤掉了对VA服务的处理,所以走正常系统流程
- KeepAliveService启动一个HiddenForeNotification,两个服务同时startForeground,且绑定同样的 ID。Stop 掉HiddenForeNotification ,这样保证了startForeground设置前台服务,而且通知栏图标也被移除。
- 参考方案二:https://blog.csdn.net/qq_37199105/article/details/81224842
public class KeepAliveService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
if(!VirtualCore.getConfig().isHideForegroundNotification()) {
HiddenForeNotification.bindForeground(this);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
}
public class HiddenForeNotification extends Service {
private static final int ID = 2781;
public static void bindForeground(Service service) {
Builder builder = NotificationChannelCompat.createBuilder(service.getApplicationContext(),
NotificationChannelCompat.DAEMON_ID);
builder.setSmallIcon(android.R.drawable.ic_dialog_dialer);
if (VERSION.SDK_INT > 24) {
builder.setContentTitle(service.getString(R.string.keep_service_damon_noti_title_v24));
builder.setContentText(service.getString(R.string.keep_service_damon_noti_text_v24));
} else {
builder.setContentTitle(service.getString(R.string.keep_service_damon_noti_title));
builder.setContentText(service.getString(R.string.keep_service_damon_noti_text));
builder.setContentIntent(PendingIntent.getService(service, 0, new Intent(service, HiddenForeNotification.class), 0));
}
builder.setSound(null);
service.startForeground(ID, builder.getNotification());
if (VERSION.SDK_INT <= 24) {
service.startService(new Intent(service, HiddenForeNotification.class));
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
Builder builder = NotificationChannelCompat.createBuilder(getBaseContext(),
NotificationChannelCompat.DAEMON_ID);
builder.setSmallIcon(android.R.drawable.ic_dialog_dialer);
builder.setContentTitle(getString(R.string.keep_service_noti_title));
builder.setContentText(getString(R.string.keep_service_noti_text));
builder.setSound(null);
startForeground(ID, builder.getNotification());
stopForeground(true);
stopSelf();
} catch (Exception e) {
e.printStackTrace();
}
return START_NOT_STICKY;
}
public IBinder onBind(Intent intent) {
return null;
}
}
参考
https://www.jianshu.com/p/ee224f18a4bd
https://blog.csdn.net/qq_37199105/article/details/81224842
https://blog.csdn.net/sinat_20059415/article/details/80997425