美文网首页Android FrameWork 学习
2.Android Binder 学习(二) bindServ

2.Android Binder 学习(二) bindServ

作者: Tsm_2020 | 来源:发表于2023-08-08 11:34 被阅读0次

    Activity通过bindService 来绑定其他进程的服务,这个期间到底进行了几次跨进程通信呢,这就需要我们来具体分析一下这里面的代码了

    点击Activity 的bindService 代码,我们就进入到了ContextWarp,并看到了如下情况

      @Override
      public boolean bindService(Intent service, ServiceConnection conn,
              int flags) {
          return mBase.bindService(service, conn, flags);
      }
    

    很明显这个所有功能都是由这个mBase 来完成的,而这个mBase 就是ContextImpl,去他里面看一下情况

      @Override
      public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
              UserHandle user) {
          return bindServiceCommon(service, conn, flags, null, mMainThread.getHandler(), null, user);
      }
    
      /** @hide */
      @Override
      public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
              Handler handler, UserHandle user) {
          if (handler == null) {
              throw new IllegalArgumentException("handler must not be null.");
          }
          return bindServiceCommon(service, conn, flags, null, handler, null, user);
      }
    
    
    
      private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
              String instanceName, Handler handler, Executor executor, UserHandle user) {
          // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
          IServiceConnection sd;
          if (conn == null) {
              throw new IllegalArgumentException("connection is null");
          }
          if (handler != null && executor != null) {
              throw new IllegalArgumentException("Handler and Executor both supplied");
          }
          if (mPackageInfo != null) {
              if (executor != null) {
                  sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
              } else {
                  sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, 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(this);
              int res = ActivityManager.getService().bindIsolatedService(
                  mMainThread.getApplicationThread(), getActivityToken(), service,
                  service.resolveTypeIfNeeded(getContentResolver()),
                  sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
              if (res < 0) {
                  throw new SecurityException(
                          "Not allowed to bind to service " + service);
              }
              return res != 0;
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
      }
    
    

    可以看到bindServiceCommon 方法标记了一些flag为离开当前进程做一些准备,同时还调用了 ActivityManager.getService().bindIsolatedService 这个方法去获取一个int 类型的res,如果你看了上一篇文章,那么就能发现 ActivityManager.getService() 他其实就是一次跨进程通信,他是通过ServiceManager 来获取 IActivityManager.Stub 的一个过程,代码如下

      @UnsupportedAppUsage
      public static IActivityManager getService() {
          return IActivityManagerSingleton.get();
      }
    
    
      @UnsupportedAppUsage
      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;
                  }
              };
    

    跨进程通信次数->1 通过ServiceManager.getService 获取到的对应的IBinder

    这里使用了一个静态单利来从ServiceManager 里面获取了 IActivityManager.Stub ,那么他获取的是谁呢,其实猜应该也能猜到,就是ActivityManagerService,我们来看一下ActivityManagerService 的继承关系

    public class ActivityManagerService extends IActivityManager.Stub
          implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {
    
    }
    

    从 ActivityManagerService 的继承关系我们可以看出来,获取到的IActivityManager.Stub 就是 ActivityManagerService ,如果我们要调用上面这个IBinder 方法其实也是使用的 跨进程通信

    跨进程通信次数->2 通过IActivityManager调用bindIsolatedService 进行第二次跨进程通信,将指令交给ActivityManagerService

    但是上一篇文章文章中我们提到过绝大部分SystemService 的子类的 Lifecycle 在执行onStart 过程中会将他初始化过程中new 的最新的Service 添加到ServiceManager来管理,但是ActivityManagerServcie 没有,但是从上面代码我们看到这个获取的过程又是这么进行的,这个问题需要我们仔细研究一下ActivityManagerServcie 的流程,目前的学习进度还没有到哪里,不过我们需要预留一个疑问

    疑问:ActivityManagerService 是何时把自己添加到ServcieManager 中的?

    上面这个疑问我们暂且先不管,我们需要知道的是 在ActivityManager 中进行了一次跨进程通信

    在这里我通过ServcieManager 获取到的也是一个

    下面代码就进入到了ActivityManagerService 中了,如下

      public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
              String resolvedType, IServiceConnection connection, int flags, String instanceName,
              String callingPackage, int userId) throws TransactionTooLargeException {
          enforceNotIsolatedCaller("bindService");
    
          // Refuse possible leaked file descriptors
          if (service != null && service.hasFileDescriptors() == true) {
              throw new IllegalArgumentException("File descriptors passed in Intent");
          }
    
          if (callingPackage == null) {
              throw new IllegalArgumentException("callingPackage cannot be null");
          }
    
          // Ensure that instanceName, which is caller provided, does not contain
          // unusual characters.
          if (instanceName != null) {
              for (int i = 0; i < instanceName.length(); ++i) {
                  char c = instanceName.charAt(i);
                  if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
                              || (c >= '0' && c <= '9') || c == '_' || c == '.')) {
                      throw new IllegalArgumentException("Illegal instanceName");
                  }
              }
          }
    
          synchronized(this) {
              return mServices.bindServiceLocked(caller, token, service,
                      resolvedType, connection, flags, instanceName, callingPackage, userId);
          }
      }
    

    可以看出来代码进入到了 mServices.bindServiceLocked 这个方法里面 这个mService 是 AMS 中成员变量 ActiveServices 的实例,并且是在AMS 创建过程中初始化的,继续向下跟踪代码

      int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
              String resolvedType, final IServiceConnection connection, int flags,
              String instanceName, String callingPackage, final int userId)
              throws TransactionTooLargeException {
              //省略部分代码
              if (s.app != null && b.intent.received) {//关键判断代码
                  // Service is already running, so we can immediately
                  // publish the connection.
                  try {
                      c.conn.connected(s.name, b.intent.binder, false);
                  } catch (Exception e) {
                      Slog.w(TAG, "Failure sending service " + s.shortInstanceName
                              + " to connection " + c.conn.asBinder()
                              + " (in " + c.binding.client.processName + ")", e);
                  }
    
                  // If this is the first app connected back to this binding,
                  // and the service had previously asked to be told when
                  // rebound, then do so.
                  if (b.intent.apps.size() == 1 && b.intent.doRebind) {//关键判断代码
                      requestServiceBindingLocked(s, b.intent, callerFg, true);
                  }
              } else if (!b.intent.requested) {//关键判断代码
                  requestServiceBindingLocked(s, b.intent, callerFg, false);
              }
    
              maybeLogBindCrossProfileService(userId, callingPackage, callerApp.info.uid);
    
              getServiceMapLocked(s.userId).ensureNotStartingBackgroundLocked(s);
    
          } finally {
              Binder.restoreCallingIdentity(origId);
          }
          return 1;
      }
    

    在这个方法里面做了好多判断,同时还做了很多flag 的设置,同时最后还进行了一个非常重要的判断,那就是被启动的s.app!=null && b.intent.received ,这段代码的意思可以理解被启动的app 是否存在,并且可以通信, 接下来的判断就更重要了,判断一下进程数为1,并且是doRebind 重新绑定的时候执行 requestServiceBindingLocked true 方法,如果不是就进入到 requestServiceBindingLocked false 方法

      private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
              boolean execInFg, boolean rebind) throws TransactionTooLargeException {
          if (r.app == null || r.app.getThread() == null) {
              // If service is not currently running, can't yet bind.
              return false;
          }
          if (DEBUG_SERVICE) Slog.d(TAG_SERVICE, "requestBind " + i + ": requested=" + i.requested
                  + " rebind=" + rebind);
          if ((!i.requested || rebind) && i.apps.size() > 0) {
              try {
                  bumpServiceExecutingLocked(r, execInFg, "bind",
                          OomAdjuster.OOM_ADJ_REASON_BIND_SERVICE);
                  r.app.getThread().scheduleBindService(r, i.intent.getIntent(), rebind,
                          r.app.mState.getReportedProcState());
                  if (!rebind) {
                      i.requested = true;
                  }
                  i.hasBound = true;
                  i.doRebind = false;
              } catch (TransactionTooLargeException e) {
                  // Keep the executeNesting count accurate.
                  if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
                  final boolean inDestroying = mDestroyingServices.contains(r);
                  serviceDoneExecutingLocked(r, inDestroying, inDestroying, false);
                  throw e;
              } catch (RemoteException e) {
                  if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
                  // Keep the executeNesting count accurate.
                  final boolean inDestroying = mDestroyingServices.contains(r);
                  serviceDoneExecutingLocked(r, inDestroying, inDestroying, false);
                  return false;
              }
          }
          return true;
      }
    
    

    这里面有一个比较重要的方法 r.app.getThread().scheduleBindService , 当前我们已经来到了系统进程,但是这个代码的意思是要在另一个app进程中 执行一个scheduleBindService 的方法,貌似这里也是一个跨进程通信,但是又没有使用Binder 的方法,暂且不论,应该是 AMS里面有所有 app 的信息,直接调用的

    这个app.getThread 是谁呢,其实这里也能猜到,就是ActivityThread ,那么我们如何证明的,
    在ActivityThread 的main 方法中有以下代码能看出来

          //之前SystemServer调用attach传入的是true,这里到应用进程传入false就行
          ActivityThread thread = new ActivityThread();
          thread.attach(false, startSeq);
    
    

    这个意思就是讲 ActivityThread 方法依赖入 SystemServer 进程当中

    我们去看一下 ActivityThread 是如何处理的scheduleBindService 这个方法的

          public final void scheduleBindService(IBinder token, Intent intent,
                  boolean rebind, int processState) {
              updateProcessState(processState, false);
              BindServiceData s = new BindServiceData();
              s.token = token;
              s.intent = intent;
              s.rebind = rebind;
    
              if (DEBUG_SERVICE)
                  Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
                          + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
              sendMessage(H.BIND_SERVICE, s);
          }
    

    这里面发送了一个bindService 的消息

    继续向下看ActivityThread 中的内部类H 是如何处理的

    在H 这个Handler 方法中又调用了 handleBindService 这个方法,如下

      private void handleBindService(BindServiceData data) {
          CreateServiceData createData = mServicesData.get(data.token);
          Service s = mServices.get(data.token);
          if (DEBUG_SERVICE)
              Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
          if (s != null) {
              try {
                  data.intent.setExtrasClassLoader(s.getClassLoader());
                  data.intent.prepareToEnterProcess(isProtectedComponent(createData.info),
                          s.getAttributionSource());
                  try {
                      if (!data.rebind) {
                          IBinder binder = s.onBind(data.intent);
                          ActivityManager.getService().publishService(
                                  data.token, data.intent, binder);
                      } else {
                          s.onRebind(data.intent);
                          ActivityManager.getService().serviceDoneExecuting(
                                  data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
                      }
                  } catch (RemoteException ex) {
                      throw ex.rethrowFromSystemServer();
                  }
              } catch (Exception e) {
                  if (!mInstrumentation.onException(s, e)) {
                      throw new RuntimeException(
                              "Unable to bind to service " + s
                              + " with " + data.intent + ": " + e.toString(), e);
                  }
              }
          }
      }
    

    在这里又调用了 ActivityManager.getService().publishService 这个方法,很明显又是一次跨进程通信,

    跨进程通信次数->3 在ServiceManager 中 获取AMS

    跨进程通信次数 4 使用AMS publishService 建立链接

    在AMS 中继续查看相关代码

      public void publishService(IBinder token, Intent intent, IBinder service) {
          // Refuse possible leaked file descriptors
          if (intent != null && intent.hasFileDescriptors() == true) {
              throw new IllegalArgumentException("File descriptors passed in Intent");
          }
    
          synchronized(this) {
              if (!(token instanceof ServiceRecord)) {
                  throw new IllegalArgumentException("Invalid service token");
              }
              mServices.publishServiceLocked((ServiceRecord)token, intent, service);
          }
      }
    

    这里与contextImpl 调用的bindServiceCommon 一样,都是交给 ActiveService 来处理的,继续跟踪

    void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
          final long origId = Binder.clearCallingIdentity();
          try {
              if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r
                      + " " + intent + ": " + service);
              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;
                      ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
                      for (int conni = connections.size() - 1; conni >= 0; conni--) {
                          ArrayList<ConnectionRecord> clist = connections.valueAt(conni);
                          for (int i=0; i<clist.size(); i++) {
                              ConnectionRecord c = clist.get(i);
                              if (!filter.equals(c.binding.intent.intent)) {
                                  if (DEBUG_SERVICE) Slog.v(
                                          TAG_SERVICE, "Not publishing to: " + c);
                                  if (DEBUG_SERVICE) Slog.v(
                                          TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);
                                  if (DEBUG_SERVICE) Slog.v(
                                          TAG_SERVICE, "Published intent: " + intent);
                                  continue;
                              }
                              if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
                              try {
                                  c.conn.connected(r.name, service, false);
                              } catch (Exception e) {
                                  Slog.w(TAG, "Failure sending service " + r.shortInstanceName
                                        + " to connection " + c.conn.asBinder()
                                        + " (in " + c.binding.client.processName + ")", e);
                              }
                          }
                      }
                  }
    
                  serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false, false);
              }
          } finally {
              Binder.restoreCallingIdentity(origId);
          }
      }
    

    在这两将 connection 与 stub 进行的链接,整个过程就结束了

    整个过程大致如下


    好像是服务器有问题,图片的话我这边稍后补充

    值得注意的是在Java 层没有看到线程调用,但是在C++层是通过 IPCThreadState 这个线程类来管理的 binder 链接,所以 所有的跨进程都会启动线程来发送消息,面试有被问到过,锁着这里特别来zhuming

    相关文章

      网友评论

        本文标题:2.Android Binder 学习(二) bindServ

        本文链接:https://www.haomeiwen.com/subject/rhynpdtx.html