当前位置: 代码迷 >> Android >> Service与Android系统设计(八)
  详细解决方案

Service与Android系统设计(八)

热度:16   发布时间:2016-05-01 13:05:41.0
Service与Android系统设计(8)

特别声明:本系列文章LiAnLab.org著作权所有,转载请注明出处。作者系LiAnLab.org资深Android技术顾问吴赫老师。本系列文章交流与讨论:@宋宝华Barry

3.4   IActiivtyManager的Stub端实现

       对于IActivityManager的Service的实现部分,因为整个Stub接口都已经在抽象类ActivityManagerNative里完成了,所以也跟aidl会有不一样之处,我们不需要创建一个Stub对象,然后再在这个Stub对象里提供具体的方法实现,而是只需要根据onTransact()解析出来的方法提供具体实现。一般的Remote Service,Stub对象是通过onBind()回调方法触发创建的,会返回一个IBinder的引用到客户端。对于IActivityManager来说没有这样的触发点,它反倒是会远程调用到这一回调方法,所以这里并不需要实现这部分代码,而是保持循环,提供这种onBind()触发能力。

       IActivityManager的Stub端,是会运行在SystemServer进程空间里,由frameworks/base/services/java/com/android/server/am/ActivityManagerService.java实现的。从前面的代码可以看出,其实ActivityManagerNative类已经将Binder接收端的代码封装好了,此时,我们所需要的,只是写一个ActivityManagerNative的实现类(因为ActivityManagerNative是一个抽象类)。

我们可以再来看看ActivityManagerService.java的实现:

public finalclass ActivityManagerServiceextends ActivityManagerNative

       implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {   1

   ...

    final Handler mHandler= newHandler() {   2

   ...

       public void handleMessage(Message msg) {

           switch (msg.what) {

           case SHOW_ERROR_MSG: { 

                HashMap data = (HashMap)msg.obj;

                synchronized(ActivityManagerService.this) {

                    ProcessRecord proc =(ProcessRecord)data.get("app");

                    if (proc != null &&proc.crashDialog != null) {

                        Slog.e(TAG, "App already has crash dialog: " + proc);

                        return;

                    }

                    AppErrorResult res =(AppErrorResult) data.get("result");

                    if (mShowDialogs &&!mSleeping && !mShuttingDown) {

                        Dialog d = newAppErrorDialog(mContext, res, proc);

                       d.show();

                        proc.crashDialog = d;

                    } else {

                        res.set(0);

                    }

                }               

                ensureBootCompleted();

           } break;

           ...

       }

    };

 

   @Override

    public boolean onTransact(int code, Parcel data,Parcel reply,int flags)  3

           throws RemoteException {

       if(code == SYSPROPS_TRANSACTION) {

           ...

       }

       try {

           return super.onTransact(code, data, reply, flags);

       } catch (RuntimeException e) {

           if(!(e instanceof SecurityException)) {

                Slog.e(TAG, "Activity Manager Crash", e);

           }

           throw e;

       }

    }

 

    static classPermissionControllerextends IPermissionController.Stub {    4

       ActivityManagerService mActivityManagerService;

       PermissionController(ActivityManagerService activityManagerService) {

           mActivityManagerService =activityManagerService;

       }

 

       public boolean checkPermission(String permission,int pid,int uid) {

           return mActivityManagerService.checkPermission(permission, pid,

                    uid) == PackageManager.PERMISSION_GRANTED;

       }

    }

 

    private class ServiceRestarterimplements Runnable {          5

       private ServiceRecord mService;

 

       void setService(ServiceRecord service) {

           mService = service;

       }

 

       public void run() {

           synchronized(ActivityManagerService.this) {

               performServiceRestartLocked(mService);

           }

       }

    }

 

    public finalint startActivity(IApplicationThread caller,    6

           Intent intent, String resolvedType, IBinder resultTo,

           String resultWho, int requestCode,int startFlags,      

StringprofileFile, ParcelFileDescriptor profileFd, Bundle options) {

       enforceNotIsolatedCaller("startActivity");

       int userId = 0;

       if(intent.getCategories() != null && intent.getCategories().contains(Intent.CATEGORY_HOME)) {

           userId = mCurrentUserId;

       } else {

           if(Binder.getCallingUid() < Process.FIRST_APPLICATION_UID) {

                userId = 0;

           } else {

                userId =Binder.getOrigCallingUser();

           }

       }

       return mMainStack.startActivityMayWait(caller, -1, intent, resolvedType,

           resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,

           null, null, options, userId);

    }

 

    public intbindService(IApplicationThread caller, IBinder token,    7

           Intent service, String resolvedType,

           IServiceConnection connection, int flags,int userId) {

       enforceNotIsolatedCaller("bindService");

       if(service != null && service.hasFileDescriptors() == true) {

           throw new IllegalArgumentException("Filedescriptors passed in Intent");

       }

 

       checkValidCaller(Binder.getCallingUid(), userId);

 

       synchronized(this) {

           final ProcessRecord callerApp = getRecordForAppLocked(caller);

           if(callerApp == null) {

                throw new SecurityException(

                        "Unable to find app for caller " + caller

                        + " (pid=" +Binder.getCallingPid()

                        + ") when binding service " + service);

           }

 

           ActivityRecord activity = null;

           if(token != null) {

                activity =mMainStack.isInStackLocked(token);

                if (activity == null) {

                    Slog.w(TAG, "Binding with unknown activity: " + token);

                    return 0;

                }

           }

 

           int clientLabel = 0;

           PendingIntent clientIntent = null;

           

           if(callerApp.info.uid == Process.SYSTEM_UID) {

                try {

                    clientIntent =(PendingIntent)service.getParcelableExtra(

                           Intent.EXTRA_CLIENT_INTENT);

                } catch (RuntimeException e) {

                }

                if (clientIntent != null) {

                   clientLabel =service.getIntExtra(Intent.EXTRA_CLIENT_LABEL,0);

                    if (clientLabel != 0) {

                        service =service.cloneFilter();

                    }

                }

           }

           

           ServiceLookupResult res =

                retrieveServiceLocked(service,resolvedType,

                        Binder.getCallingPid(),Binder.getCallingUid(), userId);

           if(res == null) {

                return 0;

           }

           if(res.record == null) {

                return -1;

           }

           if(isSingleton(res.record.processName, res.record.appInfo)) {

                userId = 0;

                res =retrieveServiceLocked(service, resolvedType, Binder.getCallingPid(),

                        Binder.getCallingUid(),0);

           }

           ServiceRecord s = res.record;

 

           final long origId = Binder.clearCallingIdentity();

 

           if(unscheduleServiceRestartLocked(s)) {

                if (DEBUG_SERVICE) Slog.v(TAG,"BIND SERVICE WHILE RESTART PENDING:"

                        + s);

           }

 

           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);

           if(clist == null) {

                clist = new ArrayList<ConnectionRecord>();

                s.connections.put(binder,clist);

           }

           clist.add(c);

           b.connections.add(c);

           if(activity != null) {

                if (activity.connections ==null) {

                    activity.connections = newHashSet<ConnectionRecord>();

                }

                activity.connections.add(c);

           }

           b.client.connections.add(c);

           if((c.flags&Context.BIND_ABOVE_CLIENT) !=0) {

                b.client.hasAboveClient = true;

           }

           clist = mServiceConnections.get(binder);

           if(clist == null) {

                clist = newArrayList<ConnectionRecord>();

                mServiceConnections.put(binder,clist);

           }

           clist.add(c);

 

           if((flags&Context.BIND_AUTO_CREATE) !=0) {

                s.lastActivity =SystemClock.uptimeMillis();

                if (!bringUpServiceLocked(s,service.getFlags(),false)) {

                    return 0;

                }

           }

 

           if(s.app != null) {

                // This could have made the service more important.

                updateOomAdjLocked(s.app);

           }

 

           if(DEBUG_SERVICE) Slog.v(TAG, "Bind" + s + "with " + b

                    + ": received=" +b.intent.received

                    + " apps=" +b.intent.apps.size()

                    + " doRebind=" +b.intent.doRebind);

 

           if(s.app != null && b.intent.received) {

                try {

                    c.conn.connected(s.name,b.intent.binder);

                } catch (Exception e) {

                    Slog.w(TAG, "Failure sending service " + s.shortName

                            + " to connection " + c.conn.asBinder()

                            + " (in " +c.binding.client.processName +")", e);

                }

 

                if (b.intent.apps.size() ==1 &&b.intent.doRebind) {

                   requestServiceBindingLocked(s, b.intent, true);

                }

           } else if (!b.intent.requested) {

                requestServiceBindingLocked(s,b.intent, false);

           }

 

           Binder.restoreCallingIdentity(origId);

       }

 

       return 1;

    }

 

    public finalint broadcastIntent(IApplicationThreadcaller,      8

           Intent intent, String resolvedType, IIntentReceiver resultTo,

           int resultCode, String resultData, Bundle map,

           String requiredPermission, boolean serialized,boolean sticky,int userId) {

       enforceNotIsolatedCaller("broadcastIntent");

       synchronized(this) {

           intent = verifyBroadcastLocked(intent);

           

           final ProcessRecord callerApp = getRecordForAppLocked(caller);

           final int callingPid = Binder.getCallingPid();

           final int callingUid = Binder.getCallingUid();

           final long origId = Binder.clearCallingIdentity();

           int res = broadcastIntentLocked(callerApp,

                    callerApp != null ?callerApp.info.packageName :null,

                    intent, resolvedType,resultTo,

                    resultCode, resultData,map, requiredPermission, serialized, sticky,

                    callingPid, callingUid,userId);

           Binder.restoreCallingIdentity(origId);

           return res;

       }

    }

 

    private finalintcomputeOomAdjLocked(ProcessRecord app,int hiddenAdj,

           ProcessRecord TOP_APP, boolean recursed,boolean doingAll) {   9

        ...

       return app.curRawAdj;

    }

 

    final voidperformAppGcLocked(ProcessRecord app) {  10

       try {

           app.lastRequestedGc = SystemClock.uptimeMillis();

           if(app.thread != null) {

                if (app.reportLowMemory) {

                    app.reportLowMemory = false;

                   app.thread.scheduleLowMemory();

                } else {

                   app.thread.processInBackground();

                }

           }

       } catch (Exception e) {

           // whatever.

       }

    }

}

1 ActivityManagerService最终会是一个实现ActivityManagerNative接口方法的类,ActivityManagerNative里带抽象标记的方法,都需要在ActivityManagerService里实现。虽然基本的Binder接收端处理也还是在ActivityManagerNative类里实现的,但由于直接使用ActivityManagerNative类的地方,并不能将这一抽象类实例化,于是在客户端实际上只能得到ActivityManagerProxy,而服务端则可以通过ActivityManagerService得到具体的IActivityManager接口的Service实例。这样的代码实现则使构建在Binder收发两端的代码逻辑都被统一起来,但同时也可以在运行时通过不同的实例化能力被拆分开。由于ActivityManager还需要提供其他部分的交互功能,于是不光是实现ActivityManagerNative抽象类,同时还会实现Watchdog.Monitor,和BatteryStatsImpl.BatteryCallback这两个接口对象。

2 Handler对象。在Android世界里,Handler对象是多线程处理上的一种异常灵活的机制,Handler会与创建它的线程,以及这一线程所附带的MessageQueue绑定。而拥有Handler对象的线程,则具备从MessageQueue中取出消息进行处理的能力,如果Message本身是一个Runnable对象,则可以在当前线程内执行某种操作。这样的机制应用程序的多线程编程时多用于后台线程回调到UI主线程里,但在Android系统里,也会大量使用这种机制,实现消息管理上的灵活处理。Handler通过postMessage()方法将Message插入MessageQueue,而通过handleMessage()将消息取出并处理,而在一个Handler对象的实现里,一般只需要实现handleMessage()方法。

3 覆盖onTransact()方法。如我们前面所述的IoC模式在设计上的妙用,于是我们每次继承某个基类时,我们都有可能通过覆盖方法+反向调用的方式实现对基类的原有方法的动态拓展。在Android系统里,这一技巧被反复使用,我们即可以在父类方法之后添加一些新的处理,也可以将新加的处理插到父类方法处理之前,唯一做不到的是在父类方法执行中插入新的处理。当然,基于Binder的收发处理上有其特殊性,在ActivityManagerService里拓展的onTransact()处理,并没有公布出来,客户端的transact()并不会分发这样的消息,于是我们也可以认为这一种类似于private限定的RPC,可以通过特殊路径来发送这样拓展出来的Message,供内部使用。

4 内部实现的IPermissionController的Stub对象。IPermissionController实际上只需要一个接口方法,checkPermission(),这一部分涉及安全的功能,最需要在Intent分发时便被处理,于是我们就在ActivityManagerService里实现。而系统其他部分,则可以统一通过对IPermissionController远程接口的访问,得到权限验证功能。

5 ServiceRestarter对象,则提供Service的“监护”功能,当某一个Service处于Bounded生命周期内,又因为出错退出,或是由于系统资源不够被回收时,ServiceRestarter对象则会将Service进行重启。

6 startActivity()。应用程序里一般会通过Context.startActivity()将Intent发送出来,但实际上会通过IActivityManager这一接口,将消息发送到ActivityManagerNative。在ActivityManagerNative的onTransact()方法里会再调用一个它自己并没有实现,而在继承它的ActivityManagerService里实现的startActivity(),也就是我们这里看到的这一方法,通过ActivityStack来找到合适的Activity来响应Intent消息的请求。

7 bindService ()。因为我们前面以RemoteService为例,于是我们这里看看bindService()的实现(Remote Service必须以BoundedService为其生存期)。与Activity不同,Service不需要栈式管理,但需要更严格的并发控制,于是可以看到在整个bindService()实现里,都使用了synchronized限制符用于线程并发同步。在整个Service引用的获取过程里,差不多都是检测当前环境里是否已经存在所需要的Service,如果没有,则尝试启动它。

8 broadcastIntent()。与bindService()类似,既然我们可以使用Intent来启动合适的Service,同理我们也可以使用Intent来驱动Broadcast Receiver。在广播消息发送时,会通过内部实现的broadcastIntentLocked()来进行互斥性的消息发送。而在broadcastIntentLocked()里,则会对系统发送的Intent进行一系列特殊的操作,然后针对 sticky方式发送的Intent进行专门的处理,最后便会调用PackageManager来取得系统里会接收这一Intent的Receiver,然后再将消息发送到这些进程的MessageQueue。

9 计算OOM_ADJ。在应用程序设计时,我们看到,Android应用程序世界里有一种永不退出的机制,而会在系统内存不够时通过一个OOM_ADJ参数来杀死合适的进程以回收内存,这里便会计算并选择合理的进程。在本质上说,进程本身会通过其是否是处于前台交互,是否与前台交互相关等来确定其优先级,但在执行过程中,还是需要根据其活跃程序来判断是否该杀死该进程,如果这一进程使用频度很高,则杀死该进程则过于浪费系统资源。于是在这一computeOomAdjLocked()方法里,会根据Activity等执行实体在调度上的时间信息来动态调整OOM的相关信息。

10 针对进程进行垃圾回收。与标准Java虚拟机不一样,Android的DalvikVM是以进程为单位来构建虚拟机执行环境的,于是系统里不再有一个统一的Java虚拟机环境,不能进行全局的垃圾回收。于是,在这个performAppGcLocked()方法里,会尝试针对某个进程回收内存。

ActivityManagerService.java实现的代码很长,而且并非独立工作,实际上它只是frameworks/base/services/java/com/android/server/am这个包的调用入口。因为IActivityManager这个接口类可以说是整个Android系统的“调度器”,涉及消息分发、进程管理。虽然这样的把这样大量功能揉合到一起的设计思路并不是很优雅,也不够低耦合,但从整个系统构建来看,这样的设计便很好地贯彻了简单高效的设计思路。从编程角度来看,事实上,ActivityManagerService类只不过是ActivityManagerNative接口的具体实现,并不难理解。从应用程序角度,它会直接通过进程内预置的ActivityManagerProxy对象(通过ContextImpl对象来找到)向IActivityManager的Binder接收端发命令,如果我们系统里任何一个进程或是线程创建一个ActivityManagerService对象,则所有基于IActivityManager接口进行RPC调用的地方,都将使用这一ActivityManagerService对象响应其执行请求。从前面对于Android的进程模型的分析我们也可以看到,完成这样功能的进程会是一个叫SystemServer的系统进程。于是,所有的分析得到的结果,便是如下这样的执行模型:


当系统里拥有一个ActivityManagerService的实例,则任何系统组成部分、或是应用程序,都可以使用bindService()取回合适的Binder,然后再通过这一Binder通信管道完成后续的通讯。在ActivityManagerService实例存在之后,我们后续的操作就可以都通过aidl的方式来进行了。这种使用bindService()的方式,我们在系统里偶尔用到,但并非最常用的一种。

为什么?因为这种方式很低效,每次调用前需要通过bindService()来驱动Service的有效bounded生命周期。这样的应用情境也存在,比如蓝牙、VPN等功能,系统只是偶尔要使用,使用时通过bindService()来初始化Service的上下文环境,不再用时便可以通过unbindService()取消对Service的引用,从而可以可以按需分配地使用系统提供的各种功能。但对于系统里的绝大部分功能来说,它的生存周期是一直存在的,在系统运行过程里,需要一直给不同执行部分提供其功能。这样的Service不需要生命周期的控制,在系统运行起来后就会一直在系统内存在,全局提供系统内功能的共享。这样的Service,我们一般可以称之为SystemService,它们不再使用bindService()来驱动,而直接通过一个叫ServiceManager的功能部件来实现。


  相关解决方案