轉載請標明出處:【顧林海的博客】
本篇文章已授權微信公衆號 顧林海 獨家發佈
Service插件化的重點是保證它的優先級,需要一個真正的Service來實現,當啓動插件Service時,就會先啓動代理Service,當這個代理Service運行起來後,在它的onStartCommand等方法裏面進行分發,執行插件Service的onCreate等方法,這種方案叫代理分發。
也就是在啓動插件Service時替換爲代理Service,什麼時候替換?通過startService方法啓動Service會調用ContextWrapper的startService方法,如下所示:
//路徑:/frameworks/base/core/java/android/content/ContextWrapper.java
public class ContextWrapper extends Context {
Context mBase;
...
@Override
public ComponentName startService(Intent service) {
return mBase.startService(service);
}
...
}
在ContextWrapper的startService方法中調用mBase的startService方法,mBase的類型是Context,而Context是一個抽象類,內部定義了很多方法以及靜態常量,它的具體實現類是ContextImpl,進入ContextImpl的startService方法:
//路徑:/frameworks/base/core/java/android/app/ContextImpl.java
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
ContextImpl的startService方法中又調用了startServiceCommon方法:
//路徑:/frameworks/base/core/java/android/app/ContextImpl.java
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
//註釋1
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
...
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
註釋1處通過ActivityManager的getService方法獲取ActivityManagerService的代理類IActivityManager,進入ActivityManager的getService方法:
//路徑:/frameworks/base/core/java/android/app/ActivityManager.java
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
getService方法通過IActivityManagerSingleton的get方法獲取IActivityManager對象,IActivityManagerSingleton是一個單例類,在create方法中從ServiceManager中獲取一個名叫“activity”的Service引用,同時也是IBinder類型的ActivityManagerService的引用,最後通過IActivityManager.Stub.asInterface方法將它轉換成IActivityManager,看到IActivityManager.Stub.asInterface這段代碼時可以知道這裏採用的是AIDL方式來實現進程間通信,也就是說服務端ActivityManagerService會實現IActivityManager.Stub類並實現相應的方法。
繼續回到ContextImpl的startServiceCommon方法:
//路徑:/frameworks/base/core/java/android/app/ContextImpl.java
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
//註釋1
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
...
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
在註釋1處獲取到ActivityManagerService的代理類IActivityManager,接着通過這個代理類向ActivityManagerService發送startService的消息。
將上面的知識點進行總結,如下圖所示:
替換代理Service可以通過Hook IActivityManager生成動態代理類來實現。
Service插件化需要一個真正的Service來實現,先在AndroidManifest.xml中註冊代理ProxyService:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.book.demo">
...
<application
...>
...
<service android:name=".ProxyService" />
</application>
</manifest>
接着啓動插件Service:
public class MainActivity extends AppCompatActivity {
private Button mBtnNormal;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initEvent();
}
private void initViews(){
mBtnNormal=findViewById(R.id.btn_normal);
}
private void initEvent(){
mBtnNormal.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(MainActivity.this,TargetService.class);
startService(intent);
}
});
}
}
這個TargetService用來模擬插件Service,不能夠直接啓動,因此需要Hook IActivityManager,定義替換IActivityManager的代理類IActivityManagerProxy,代碼如下:
public class IActivityManagerProxy implements InvocationHandler {
private Object mActivityManager;
public IActivityManagerProxy(Object activityManager){
this.mActivityManager=activityManager;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("startService".equals(method.getName())){
Intent intent=null;
int index=0;
for(int i=0,length=args.length;i<length;i++){
if(args[i] instanceof Intent){
index=i;
break;
}
}
intent= (Intent) args[index];
Intent proxyIntent=new Intent();
String packageName="com.book.demo";
proxyIntent.setClassName(packageName,packageName+".ProxyService");
//將插件Service的ClassName保存起來
proxyIntent.putExtra("TARGET_SERVICE",intent.getComponent().getClassName());
//替換爲新建的proxyIntent
args[index]=proxyIntent;
}
return method.invoke(mActivityManager,args);
}
}
上述代碼中通過攔截startService方法,獲取啓動TargetService的Intent,再創建代理Service的Intent,將TargetService的Intent的相關信息保存在代理Service的Intent中,最後將啓動的Intent替換成代理Service的Intent,也就是說最後啓動的是ProxyService。
接着用IActivityManagerProxy替換系統的IActivityManager,代碼如下:
public class HookUtil {
public static void hookAMS() {
Object defaultSingleton = null;
try {
if (Build.VERSION.SDK_INT >= 26) {
Class<?> activityManagerClass = Class.forName("android.app.ActivityManager");
Field field=activityManagerClass.getDeclaredField("IActivityManagerSingleton");
field.setAccessible(true);
defaultSingleton=field.get(null);
} else {
Class<?> activityManagerNativeClass=Class.forName("android.app.ActivityManagerNative");
Field field=activityManagerNativeClass.getDeclaredField("gDefault");
field.setAccessible(true);
defaultSingleton=field.get(null);
}
Class<?> singletonClass=Class.forName("android.util.Singleton");
Field mInstanceField=singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//獲取IActivityManager
Object iActivityManager=mInstanceField.get(defaultSingleton);
Class<?> iActivityManagerClazz=Class.forName("android.app.IActivityManager");
Object proxy=Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[]{iActivityManagerClazz},new IActivityManagerProxy(iActivityManager));
mInstanceField.set(defaultSingleton,proxy);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
上述代碼中主要做了這些事:
- 對版本進行區分,最終獲取的是Singleton類型的IActivityManagerSingleton或者gDefault字段。
- 獲取Singleton類中的mInstance字段並獲取系統的IActivityManager。
- 創建代理類IActivityManagerProxy,來替換系統的IActivityManager。
接着在Application中調用這個方法:
public class MyApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
HookUtil.hookAMS();
}
}
到這裏啓動的Service不是插件的Service,而是代理的Service,接下來需要在ProxyService中進行代理分發。
public class ProxyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (null == intent || !intent.hasExtra("TARGET_SERVICE")) {
return START_STICKY;
}
String serviceName = intent.getStringExtra("TARGET_SERVICE");
if (TextUtils.isEmpty(serviceName)) {
return START_STICKY;
}
//=============反射調用插件Service的attach方法===========================
try {
//獲取attach方法需要的ActivityThread
Class activityThreadClazz = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThread = activityThreadClazz.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThread.setAccessible(true);
Object activityThread = sCurrentActivityThread.get(null);
//獲取attach方法需要的token
Method getActivityThreadMethod = activityThreadClazz.getDeclaredMethod("getApplicationThread");
getActivityThreadMethod.setAccessible(true);
Object applicationThread = getActivityThreadMethod.invoke(activityThread);
Class iInterfaceClazz = Class.forName("android.os.IInterface");
Method asBinderMethod = iInterfaceClazz.getDeclaredMethod("asBinder");
asBinderMethod.setAccessible(true);
Object token = asBinderMethod.invoke(applicationThread);
//反射獲取attach方法
Class serviceClazz = Class.forName("android.app.Service");
Method attachMethod = serviceClazz.getDeclaredMethod("attach",
Context.class, activityThreadClazz, String.class, IBinder.class, Application.class, Object.class);
attachMethod.setAccessible(true);
//獲取IActivityManager
Object defaultSingleton = null;
if (Build.VERSION.SDK_INT >= 26) {
Class<?> activityManagerClass = Class.forName("android.app.ActivityManager");
Field field = activityManagerClass.getDeclaredField("IActivityManagerSingleton");
field.setAccessible(true);
defaultSingleton = field.get(null);
} else {
Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
Field field = activityManagerNativeClass.getDeclaredField("gDefault");
field.setAccessible(true);
defaultSingleton = field.get(null);
}
Class<?> singletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
Object iActivityManager = mInstanceField.get(defaultSingleton);
//反射執行插件Service
Service targetService = (Service) Class.forName(serviceName).newInstance();
attachMethod.invoke(targetService, this,
activityThread,
intent.getComponent().getClassName(),
token,
getApplication(),
iActivityManager);
//執行插件Service的onCreate、onStartCommand方法
targetService.onCreate();
targetService.onStartCommand(intent,flags,startId);
} catch (ClassNotFoundException e) {
e.printStackTrace();
return START_STICKY;
} catch (NoSuchMethodException e) {
e.printStackTrace();
return START_STICKY;
} catch (NoSuchFieldException e) {
e.printStackTrace();
return START_STICKY;
} catch (IllegalAccessException e) {
e.printStackTrace();
return START_STICKY;
} catch (InvocationTargetException e) {
e.printStackTrace();
return START_STICKY;
} catch (InstantiationException e) {
e.printStackTrace();
return START_STICKY;
}
return START_STICKY;
}
}
上訴代碼主要做了以下幾件事:
- 判斷參數條件不滿足、出現異常和代碼執行完畢返回START_STICKY,這樣ProxyService會重新創建並執行onStartCommand方法。
- 創建插件Service,並反射調用attach方法。
- 進行代理分發,執行插件Service的onCreate和onStartCommand方法。
插件TargetService如下所示:
public class TargetService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.e("TargetService","----onCreate-----");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("TargetService","----onStartCommand-----");
return super.onStartCommand(intent, flags, startId);
}
}
運行看是否會在onCreate和onStartCommand方法中打印Log: