1. 程式人生 > >android service外掛化之一

android service外掛化之一

1, 概述

本文將探討Android四大元件之一——Service元件的外掛化方式。

與Activity, BroadcastReceiver相比,Service元件的不同點在哪裡呢?能否用與之相同的方式實現Service的外掛化?

如果不行,它們的差別在哪裡,應該如何實現Service的外掛化?

接下來將圍繞這幾個問題展開,最終給出Service元件的外掛化方式;

閱讀本文之前,可以先clone一份understand-plugin-framework,參考此專案的service-management模組。本編文章的原始碼基於android 6.0.

2, Service工作原理

連Service的工作原理都不瞭解,談何外掛化?知己知彼。

Service分為兩種形式:以startService啟動的服務和用bindService繫結的服務;

由於這兩個過程大體相似,這裡以稍複雜的bindService為例分析Service元件的工作原理。

    繫結Service的過程是通過Context類的bindService完成的,這個方法需要三個引數:

第一個引數代表想要繫結的Service的Intent,第二個引數是一個ServiceConnetion,

可以通過這個物件接收到Service繫結成功或者失敗的回撥;第三個引數則是繫結時候的一些FLAG;

Context的具體實現在ContextImpl類,ContextImpl中的bindService方法直接呼叫了bindServiceCommon方法,此方法原始碼如下:

private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
        UserHandle user) {
    IServiceConnection sd;
    if (conn == null) {
        throw new IllegalArgumentException("connection is null");
    }
    if (mPackageInfo != null) {
        // important
        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
                mMainThread.getHandler(), flags);
    } else {
        throw new RuntimeException("Not supported in system context");
    }
    validateServiceIntent(service);
    try {
        IBinder token = getActivityToken();
        if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
                && mPackageInfo.getApplicationInfo().targetSdkVersion
                < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            flags |= BIND_WAIVE_PRIORITY;
        }
        service.prepareToLeaveProcess();
        int res = ActivityManagerNative.getDefault().bindService(
            mMainThread.getApplicationThread(), getActivityToken(), service,
            service.resolveTypeIfNeeded(getContentResolver()),
            sd, flags, getOpPackageName(), user.getIdentifier());
        if (res < 0) {
            throw new SecurityException(
                    "Not allowed to bind to service " + service);
        }
        return res != 0;
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}

大致觀察就能發現這個方法最終通過ActivityManagerNative藉助AMS進而完成Service的繫結過程,

在跟蹤AMS的bindService原始碼之前,關注一下這個方法開始處建立的sd變數。這個變數的型別是IServiceConnection,

如果讀者還有印象,在廣播的管理一文中也遇到過類似的處理方式——IIntentReceiver;

所以,這個IServiceConnection與 IApplicationThread以及IIntentReceiver相同,

都是ActivityThread給AMS提供的用來與之進行通訊的 Binder物件;這個介面的實現類為LoadedApk.ServiceDispatcher。

這個方法最終呼叫了ActivityManagerNative的bindService,而這個方法的真正實現在AMS裡面,原始碼如下:

public int bindService(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, IServiceConnection connection, int flags, String callingPackage,
        int userId) throws TransactionTooLargeException {
    enforceNotIsolatedCaller("bindService");
    // 略去引數校檢
    synchronized(this) {
        return mServices.bindServiceLocked(caller, token, service,
                resolvedType, connection, flags, callingPackage, userId);
    }
}

bindService這個方法相當簡單,只是做了一些引數校檢之後直接呼叫了ActivityServices類的bindServiceLocked方法:

int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
        String resolvedType, IServiceConnection connection, int flags,
        String callingPackage, int userId) throws TransactionTooLargeException {
    final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
    // 引數校檢,略

    ServiceLookupResult res =
        retrieveServiceLocked(service, resolvedType, callingPackage,
                Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
    // 結果校檢, 略
    ServiceRecord s = res.record;

    final long origId = Binder.clearCallingIdentity();

    try {
        // ... 不關心, 略

        mAm.startAssociationLocked(callerApp.uid, callerApp.processName,
                s.appInfo.uid, s.name, s.processName);

        AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
        ConnectionRecord c = new ConnectionRecord(b, activity,
                connection, flags, clientLabel, clientIntent);
        IBinder binder = connection.asBinder();
        ArrayList<ConnectionRecord> clist = s.connections.get(binder);

        // 對connection進行處理, 方便存取,略
        clist.add(c);

        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
            s.lastActivity = SystemClock.uptimeMillis();
            if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
                return 0;
            }
        }

        // 與BIND_AUTO_CREATE不同的啟動FLAG,原理與後續相同,略

    } finally {
        Binder.restoreCallingIdentity(origId);
    }

    return 1;
}

這個方法比較長,這裡省去了很多無關程式碼,只列出關鍵邏輯;首先它通過retrieveServiceLocked方法

獲取到了intent匹配到的需要bind到的Service元件res;然後把ActivityThread傳遞過來的IServiceConnection

使用ConnectionRecord進行了包裝,方便接下來使用;最後如果啟動的FLAG為BIND_AUTO_CREATE,

那麼呼叫bringUpServiceLocked開始建立Service,跟蹤這個方法:(非這種FLAG的程式碼已經省略,可以自行跟蹤)

private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
        boolean whileRestarting) throws TransactionTooLargeException {
    // 略。。
    final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
    final String procName = r.processName;
    ProcessRecord app;
    if (!isolated) {
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
        if (app != null && app.thread != null) {
            try {
                app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                // 1. important !!!
                realStartServiceLocked(r, app, execInFg);
                return null;
            } catch (TransactionTooLargeException e) {
                throw e;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting service " + r.shortName, e);
            }
        }
    } else {
        app = r.isolatedProc;
    }
    // Not running -- get it started, and enqueue this service record
    // to be executed when the app comes up.
    if (app == null) {
        // 2. important !!!
        if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                "service", r.name, false, isolated, false)) == null) {
            bringDownServiceLocked(r);
            return msg;
        }
        if (isolated) {
            r.isolatedProc = app;
        }
    }
    // 略。。
    return null;
}

這個方案同樣也很長,但是實際上非常簡單:注意註釋的兩個important的地方,

如果Service所在的程序已經啟動,那麼直接呼叫realStartServiceLocked方法來真正啟動Service元件;

如果Service所在的程序還沒有啟動,那麼先在AMS中記下這個要啟動的Service元件,然後通過startProcessLocked啟動新的程序。

先看Service程序已經啟動的情況,也即realStartServiceLocked分支:

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    
    // 略。。

    boolean created = false;
    try {
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startLaunchedLocked();
        }
        mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        // 略。。
    }

    requestServiceBindingsLocked(r, execInFg);

    // 不關心,略。。
}

這個方法首先呼叫了app.thread的scheduleCreateService方法,知道,這是一個IApplicationThread物件,

它是App所在程序提供給AMS的用來與App程序進行通訊的Binder物件,這個Binder的 Server端在

ActivityThread的ApplicationThread類,因此,跟蹤ActivityThread類,這個方法的實現如下:

public final void scheduleCreateService(IBinder token,
        ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
    updateProcessState(processState, false);
    CreateServiceData s = new CreateServiceData();
    s.token = token;
    s.info = info;
    s.compatInfo = compatInfo;

    sendMessage(H.CREATE_SERVICE, s);
}

它不過是轉發了一個訊息給ActivityThread的H這個Handler,H類收到這個訊息之後,

直接呼叫了ActivityThread類的handleCreateService方法,如下:

private void handleCreateService(CreateServiceData data) {
    unscheduleGcIdler();

    LoadedApk packageInfo = getPackageInfoNoCheck(
            data.info.applicationInfo, data.compatInfo);
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance();
    } catch (Exception e) {
    }

    try {
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        context.setOuterContext(service);

        Application app = packageInfo.makeApplication(false, mInstrumentation);
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManagerNative.getDefault());
        service.onCreate();
        mServices.put(data.token, service);
        try {
            ActivityManagerNative.getDefault().serviceDoneExecuting(
                    data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
        } catch (RemoteException e) {
            // nothing to do.
        }
    } catch (Exception e) {
    }
}

看到這段程式碼,是不是似曾相識?!沒錯,這裡與Activity元件的建立過程如出一轍!

所以這裡就不贅述了,可以參閱 Activity生命週期管理。

需要注意的是,這裡Service類的建立過程與Activity是略微有點不同的,雖然都是通過ClassLoader通過反射建立,

但是Activity卻把建立過程委託給了Instrumentation類,而Service則是直接進行。

OK,現在ActivityThread裡面的handleCreateService方法成功創建出了Service物件,並且呼叫了它的onCreate方法;

到這裡的Service已經啟動成功。scheduleCreateService這個Binder呼叫過程結束,

程式碼又回到了AMS程序的realStartServiceLocked方法。這裡不得不感嘆Binder機制的精妙,

如此簡潔方便高效的跨程序呼叫,在程序之間來回穿梭,遊刃有餘。

realStartServiceLocked方法的程式碼如下:

private final void realStartServiceLocked(ServiceRecord r,
        ProcessRecord app, boolean execInFg) throws RemoteException {
    
    // 略。。

    boolean created = false;
    try {
        synchronized (r.stats.getBatteryStats()) {
            r.stats.startLaunchedLocked();
        }
        mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
        app.thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                app.repProcState);
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        mAm.appDiedLocked(app);
        throw e;
    } finally {
        // 略。。
    }
    requestServiceBindingsLocked(r, execInFg);
    // 不關心,略。。
}

這個方法在完成scheduleCreateService這個binder呼叫之後,執行了一個requestServiceBindingsLocked方法;

看方法名好像於「繫結服務」有關,它簡單地執行了一個遍歷然後呼叫了另外一個方法:

private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
        boolean execInFg, boolean rebind) throws TransactionTooLargeException {
    if (r.app == null || r.app.thread == null) {
        return false;
    }
    if ((!i.requested || rebind) && i.apps.size() > 0) {
        try {
            bumpServiceExecutingLocked(r, execInFg, "bind");
            r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
            r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
                    r.app.repProcState);
        // 不關心,略。。
    }
    return true;
}

可以看到,這裡又通過IApplicationThread這個Binder進行了一次IPC呼叫,

跟蹤ActivityThread類裡面的ApplicationThread的scheduleBindService方法,

發現這個方法不過通過Handler轉發了一次訊息,真正的處理程式碼在handleBindService裡面:

private void handleBindService(BindServiceData data) {
    Service s = mServices.get(data.token);
    if (s != null) {
        try {
            data.intent.setExtrasClassLoader(s.getClassLoader());
            data.intent.prepareToEnterProcess();
            try {
                if (!data.rebind) {
                    IBinder binder = s.onBind(data.intent);
                    ActivityManagerNative.getDefault().publishService(
                            data.token, data.intent, binder);
                } else {
                    s.onRebind(data.intent);
                    ActivityManagerNative.getDefault().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                }
                ensureJitEnabled();
            } catch (RemoteException ex) {
            }
        } catch (Exception e) {
        }
    }
}

要Bind的Service終於在這裡完成了繫結!繫結之後又通過ActivityManagerNative這個Binder進行一次IPC呼叫,

檢視AMS的publishService方法,這個方法簡單第呼叫了publishServiceLocked方法,原始碼如下:

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    final long origId = Binder.clearCallingIdentity();
    try {
        if (r != null) {
            Intent.FilterComparison filter
                    = new Intent.FilterComparison(intent);
            IntentBindRecord b = r.bindings.get(filter);
            if (b != null && !b.received) {
                b.binder = service;
                b.requested = true;
                b.received = true;
                for (int conni=r.connections.size()-1; conni>=0; conni--) {
                    ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                    for (int i=0; i<clist.size(); i++) {
                        ConnectionRecord c = clist.get(i);
                        if (!filter.equals(c.binding.intent.intent)) {
                            continue;
                        }
                        try {
                            c.conn.connected(r.name, service);
                        } catch (Exception e) {
                        }
                    }
                }
            }

            serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
        }
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}

還記得之前提到的那個IServiceConnection嗎?在bindServiceLocked方法裡面, 

把這個IServiceConnection放到了一個ConnectionRecord的List中存放在ServiceRecord裡面,

這裡所做的就是取出已經被Bind的這個Service對應的IServiceConnection物件,然後呼叫它的connected方法;

這個IServiceConnection也是一個Binder物件,它的Server端在LoadedApk.ServiceDispatcher裡面。

程式碼到這裡已經很明確了,接下來的過程非常簡單,感興趣的讀者自行查閱LoadedApk.ServiceDispatcher的connected方法,

一路跟蹤弄清楚ServiceConnection回撥過程,完成最後的拼圖!

最後提一點,以上分析了Service所在程序已經存在的情況,如果Service所在程序不存在,

那麼會呼叫startProcessLocked方法建立一個新的程序,並把需要啟動的Service放在一個佇列裡面;

建立程序的過程通過Zygote fork出來,程序建立成功之後會呼叫ActivityThread的main方法,

在這個main方法裡面間接呼叫到了AMS的 attachApplication方法,在AMS的attachApplication裡面

會檢查剛剛那個待啟動Service佇列裡面的內容,並執行 Service的啟動操作;

之後的啟動過程與程序已經存在的情況下相同;可以自行分析。