소스 각도 에서 Accessibility Service 를 보다

간단 한 소개
Accessibility Service 의 디자인 은 신체 적 결함 이 있 는 단체 가 안 드 로 이 드 애플 리 케 이 션 을 사용 하 는 것 을 보조 하기 위 한 것 으로 안 드 로 이 드 의 컨트롤 트 리 View,ViewGroup,ViewRootImpl 체 계 를 관통 시 켰 다.system 의 힘 을 빌리다server 프로 세 스 의 중계,Accessibility 이 벤트 를 등록 할 수 있 는 클 라 이언 트 는 systemserver 가 제공 하 는 Accessibility 서 비 스 는 다른 응용 보 기 를 감청 하고 조작 하 는 기능 을 실현 합 니 다.이 기능 은 매우 강력 해서 사용자 의 행동 을 모 의하여 다른 앱 을 조작 할 수 있 으 며 자동화 테스트,위 챗 보너스 쟁탈,자동 답장 등 기능 의 실현 에 자주 사용 된다.
이 글 을 쓰 는 취 지 는 두 가지 가 있다.
4.567917.이전에 안 드 로 이 드 View 컨트롤 트 리 의 그리 기,사건 배포 의 소스 코드 분석 을 완 성 했 고 지식 비축 이 충분 합 니 다4.567917.최근 에 자동화 분야 의 프로젝트 를 접 했 고 무장 애 서 비 스 를 이용 하여 이 루어 진 자동 위 챗 보너스 쟁탈 기능 원리 에 대해 매우 궁금 하 다.
전체 도
아 날로 그 그래프
  • Accessibility Service:APP 측 이 직접 계승 하 는 유형 은 본질 적 으로 Service 이 고 onBind 를 통 해 익명 의 Binder 대상 을 얻어 통신 을 실현 합 니 다
  • IAccessibility ServiceClient Wrapper:systemserver 통신 의 익명 Binder 서비스
    Accessibility InteractionClient:본질 적 으로 binder 서비스 로 Node 정 보 를 얻 는 데 사 용 됩 니 다Accessibility Manager Service:system 에서 실행server 의 실명 binder 서 비 스 는 전체적인 관리 류 입 니 다
  • 서비스:AccessibilityManagerService 의 내부 클래스 는 AccessibilityInteractionClient 의 binder 통신 요청 에 응답 하 는 데 사 용 됩 니 다
  • Accessibility InteractionConnection:모니터링 되 는 앱 에서 실행 되 며 찾기,클릭 보기 등 서 비 스 를 제공 합 니 다
  • Accessibility Manager:각 APP 에서 실행 되 며 보기 변화 이 벤트 를 보 내 는 데 사 용 됩 니 다
  • Accessibility InteractionController:구체 적 인 보기 찾기,클릭 서비스의 중간 컨트롤 러
  • Accessibility NodeProvider:클 라 이언 트 가 실현 하 는 보기 노드 내용 제공 자,최종 작업 의 실현 자
  • 전체 설계도

    인 스 턴 스 코드
    
    public class AutoDismissService extends AccessibilityService {
     @Override
     public void onAccessibilityEvent(AccessibilityEvent event) {
     if (event == null) {
     return;
     }
     
     //    android       crash dialog  
     dismissAppErrorDialogIfExists(event);
     }
     
     private void dismissAppErrorDialogIfExists(AccessibilityEvent event) {
     // WINDOW           
     if ((event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
     && event.getPackageName().equals("android")) {
     //     "OK"      Node
     AccessibilityNodeInfo nodeInfo = findViewByText("OK", true);
     if (nodeInfo != null) {
     //           
     performViewClick(nodeInfo);
     }
     }
     public AccessibilityNodeInfo findViewByText(String text, boolean clickable) {
     //          
     AccessibilityNodeInfo accessibilityNodeInfo = getRootInActiveWindow();
     if (accessibilityNodeInfo == null) {
     return null;
     }
     //             
     List<AccessibilityNodeInfo> nodeInfoList = accessibilityNodeInfo.findAccessibilityNodeInfosByText(text);
     if (nodeInfoList != null && !nodeInfoList.isEmpty()) {
     for (AccessibilityNodeInfo nodeInfo : nodeInfoList) {
     if (nodeInfo != null && (nodeInfo.isClickable() == clickable)) {
      return nodeInfo;
     }
     }
     }
     return null;
     }
     
     public void performViewClick(AccessibilityNodeInfo nodeInfo) {
     if (nodeInfo == null) {
     return;
     }
     //         ,           
     while (nodeInfo != null) {
     if (nodeInfo.isClickable()) {
     nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
     break;
     }
     nodeInfo = nodeInfo.getParent();
     }
     }
    }
    이상 은 Accessibility 기능 을 실현 하 는 전형 적 인 JAVA 코드 로 주로 세 가지 기능 과 관련된다.
  • 시스템 에 보기 변화 가 있 으 면 onAccessibility Event 방법 은 자동 으로 systemserver 호출
  • Accessibility Service 의 getRoot InActiveWindow 와 find Accessibility NodeInfos ByText 방법 을 통 해 노드 정 보 를 얻 을 수 있 습 니 다
  • Accessibility NodeInfo 의 performAction 방법 을 통 해 최종 적 으로 감 청 된 앱 에서 해당 동작 을 수행 합 니 다
  • 이 글 은 이 세 가지 주요 기능 을 중심 으로 소스 코드 분석 을 할 것 이다.
    소스 코드 분석
    일반적인 AccessibilityEvent 이벤트 종류
    번호
    종류 이름
    촉발 시기
    1
    TYPE_VIEW_CLICKED
    클릭 가능 한 구성 요소 가 클릭 됨
    2
    TYPE_VIEW_LONG_CLICKED
    클릭 가능 한 구성 요소 길 게 누 르 기
    3
    TYPE_VIEW_SELECTED
    구성 요소 가 선택 됨
    4
    TYPE_VIEW_FOCUSED
    구성 요소 가 초점 을 가 져 왔 습 니 다.
    5
    TYPE_VIEW_TEXT_CHANGED
    구성 요소 의 텍스트 변화
    6
    TYPE_VIEW_SCROLLED
    구성 요소 가 미끄러지다
    7
    TYPE_WINDOW_STATE_CHANGED
    dialog 등 이 열 립 니 다.
    8
    TYPE_NOTIFICATION_STATE_CHANGED
    알림 팝 업
    9
    TYPE_WINDOW_CONTENT_CHANGED
    구성 요소 트 리 에 변화 가 생 겼 습 니 다.
    onAccessibilityEvent 트리거 프로 세 스
    여기 서 TextView.setText 트리거 이벤트 변화 프로 세 스 를 예 로 들 어 분석 합 니 다.
    TextView.setText
    응용 구성 요소 상태 변화
    frameworks/base/core/java/android/widget/TextView.java
    
    private void setText(CharSequence text, BufferType type,
       boolean notifyBefore, int oldlen) {
     ...
     notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT);
     ...   
    }
    public void notifyViewAccessibilityStateChangedIfNeeded(int changeType) {
     if (!AccessibilityManager.getInstance(mContext).isEnabled() || mAttachInfo == null) {
     return;
     }
     if (mSendViewStateChangedAccessibilityEvent == null) {
     //       Runnable,               
     mSendViewStateChangedAccessibilityEvent =
      new SendViewStateChangedAccessibilityEvent();
     }
     mSendViewStateChangedAccessibilityEvent.runOrPost(changeType);
    }
    private class SendViewStateChangedAccessibilityEvent implements Runnable {
     ...
     @Override
     public void run() {
     mPosted = false;
     mPostedWithDelay = false;
     mLastEventTimeMillis = SystemClock.uptimeMillis();
     if (AccessibilityManager.getInstance(mContext).isEnabled()) {
      final AccessibilityEvent event = AccessibilityEvent.obtain();
      event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
      event.setContentChangeTypes(mChangeTypes);
      //   TYPE_WINDOW_CONTENT_CHANGED         
      sendAccessibilityEventUnchecked(event);
     }
     mChangeTypes = 0;
     }
     ...
    }
    public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
     if (mAccessibilityDelegate != null) {
     mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event);
     } else {
     sendAccessibilityEventUncheckedInternal(event);
     }
    }
    public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
     host.sendAccessibilityEventUncheckedInternal(event);
    }
    public void sendAccessibilityEventUncheckedInternal(AccessibilityEvent event) {
     if (!isShown()) {
     return;
     }
     ...
     //     TextView   View    ,      ,          ,    ViewRootImpl    
     ViewParent parent = getParent();
     if (parent != null) {
     getParent().requestSendAccessibilityEvent(this, event);
     }
    }
    ViewRootImpl.requestSendAccessibilityEvent
    ViewRootImpl 이 사건 을 system 에 보 냅 니 다.server
    frameworks/base/core/java/android/view/ViewRootImpl.java
    
    @Override
    public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event) {
     ...
     //      AccessibilityManager      
     mAccessibilityManager.sendAccessibilityEvent(event);
     return true;
    }
    frameworks/base/core/java/android/view/accessibility/AccessibilityManager.java
    
    public void sendAccessibilityEvent(AccessibilityEvent event) {
     final IAccessibilityManager service;
     final int userId;
     synchronized (mLock) {
     //   system_server Accessibility    
     service = getServiceLocked();
     ...
     }
     
     try {
     ...
     long identityToken = Binder.clearCallingIdentity();
     // binder call     ,        
     doRecycle = service.sendAccessibilityEvent(event, userId);
     Binder.restoreCallingIdentity(identityToken);
     ...
     } catch (RemoteException re) {
     Log.e(LOG_TAG, "Error during sending " + event + " ", re);
     } finally {
     ...
     }
    }
    AccessibilityManagerService.sendAccessibilityEvent
    system_server 는 이 벤트 를 각 감청 구성 요소 가 변 하 는 Service 에 배포 합 니 다.
    frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
    
    // binder call     ,      
    @Override
    public boolean sendAccessibilityEvent(AccessibilityEvent event, int userId) {
     synchronized (mLock) {
     ...
     if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
      ...
      notifyAccessibilityServicesDelayedLocked(event, false);
      notifyAccessibilityServicesDelayedLocked(event, true);
     }
     ...
     }
     return (OWN_PROCESS_ID != Binder.getCallingPid());
    }
    private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
     boolean isDefault) {
     try {
     UserState state = getCurrentUserStateLocked();
     for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {
      Service service = state.mBoundServices.get(i);
      if (service.mIsDefault == isDefault) {
      if (canDispatchEventToServiceLocked(service, event)) {
       //       ,       
       service.notifyAccessibilityEvent(event);
      }
      }
     }
     } catch (IndexOutOfBoundsException oobe) {
     ...
     }
    }
    class Service extends IAccessibilityServiceConnection.Stub
     implements ServiceConnection, DeathRecipient {
     public void notifyAccessibilityEvent(AccessibilityEvent event) {
     synchronized (mLock) {
      ...
      if ((mNotificationTimeout > 0)
       && (eventType != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED)) {
      ...
      //     ,            
      message = mEventDispatchHandler.obtainMessage(eventType);
      } else {
      message = mEventDispatchHandler.obtainMessage(eventType, newEvent);
      }
      mEventDispatchHandler.sendMessageDelayed(message, mNotificationTimeout);
     }
     } 
    }
    public Handler mEventDispatchHandler = new Handler(mMainHandler.getLooper()) {
     @Override
     public void handleMessage(Message message) {
     final int eventType = message.what;
     AccessibilityEvent event = (AccessibilityEvent) message.obj;
     notifyAccessibilityEventInternal(eventType, event);
     }
    };
    private void notifyAccessibilityEventInternal(int eventType, AccessibilityEvent event) {
     IAccessibilityServiceClient listener;
     ...
     // mServiceInterface   bind    AccessibilityService, onServiceConnected     ,   binder proxy    ,        system_server       
     listener = mServiceInterface;
     ...
     try {
     listener.onAccessibilityEvent(event);
     if (DEBUG) {
      Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
     }
     } catch (RemoteException re) {
     Slog.e(LOG_TAG, "Error during sending " + event + " to " + listener, re);
     } finally {
     event.recycle();
     }
    }
    AccessibilityService.onAccessibilityEvent
    APP 는 구성 요소 가 변 하 는 이 벤트 를 수신 하고 해당 하 는 처 리 를 선택 할 수 있 습 니 다.
    frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java
    
    //     ,    ,       
    public abstract void onAccessibilityEvent(AccessibilityEvent event);
    //  service  system_server     ,   IAccessibilityServiceClientWrapper proxy          
    @Override
    public final IBinder onBind(Intent intent) {
     return new IAccessibilityServiceClientWrapper(this, getMainLooper(), new Callbacks() {
     ...
     @Override
     public void onAccessibilityEvent(AccessibilityEvent event) {
      AccessibilityService.this.onAccessibilityEvent(event);
     }
     ...
     }
    }
    //   binder   ,  handler         
    public void onAccessibilityEvent(AccessibilityEvent event) {
     Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);
     mCaller.sendMessage(message);
    }
    @Override
    public void executeMessage(Message message) {
     switch (message.what) {
     case DO_ON_ACCESSIBILITY_EVENT: {
      AccessibilityEvent event = (AccessibilityEvent) message.obj;
      if (event != null) {
      AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
      //            
      mCallback.onAccessibilityEvent(event);
      ...
      }
     } return;
     }
    }
    getRootInActiveWindow 부모 노드 가 져 오기 프로 세 스
    find Accessibility NodeInfosByText 를 호출 하기 전에 getRootInActiveWindow 방법 으로 부모 노드 를 가 져 와 야 부모 Accessibility NodeInfo 를 호출 하 는 방법 으로 하위 노드 정 보 를 조회 할 수 있 습 니 다.
    AccessibilityService.getRootInActiveWindow
    frameworks/base/core/java/android/accessibilityservice/AccessibilityService.java
    
    public AccessibilityNodeInfo getRootInActiveWindow() {
     //                   ,          Client       
     return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId);
    }
    frameworks/base/core/java/android/view/accessibility/AccessibilityInteractionClient.java
    
    public AccessibilityNodeInfo getRootInActiveWindow(int connectionId) {
     return findAccessibilityNodeInfoByAccessibilityId(connectionId,
      AccessibilityNodeInfo.ACTIVE_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID,
      false, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
    }
    public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
      int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache,
      int prefetchFlags) {
     ...
     //   binder call system_server,       APP          ,      AccessibilityInteractionClient    binder   , this  system_server ,              binder proxy,     
     final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId(
       accessibilityWindowId, accessibilityNodeId, interactionId, this,
       prefetchFlags, Thread.currentThread().getId());
     Binder.restoreCallingIdentity(identityToken);
     // If the scale is zero the call has failed.
     if (success) {
     //      ,           
     List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
      interactionId);
     finalizeAndCacheAccessibilityNodeInfos(infos, connectionId);
     if (infos != null && !infos.isEmpty()) {
      return infos.get(0);
     }
     } 
     ... 
    }
    Service.findAccessibilityNodeInfoByAccessibilityId
    여기 서 비 스 는 안 드 로 이 드 의 4 대 구성 요소 인 Service 가 아 닙 니 다.Accessiblit Manager Service Internal 이 라 고 부 르 는 것 이 더 적합 합 니 다.
    frameworks/base/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
    
    @Override
    public boolean findAccessibilityNodeInfoByAccessibilityId(
     int accessibilityWindowId, long accessibilityNodeId, int interactionId,
     IAccessibilityInteractionConnectionCallback callback, int flags,
     long interrogatingTid) throws RemoteException {
     ...
     //      APP       
     IAccessibilityInteractionConnection connection = null;
     ...
     resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
     ...
     if (!permissionGranted) {
     return false;
     } else {
     connection = getConnectionLocked(resolvedWindowId);
     if (connection == null) {
      return false;
     }
     }
     ...
     //    callback        proxy  ,                    system_server  ,      APP APP     
     connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityNodeId,
      partialInteractiveRegion, interactionId, callback, mFetchFlags | flags,
      interrogatingPid, interrogatingTid, spec);
     ...
    }
    AccessibilityInteractionConnection.findAccessibilityNodeInfoByAccessibilityId
    여기 서 APP 단 으로 호출 되 었 습 니 다.사실은 onAccessibility Event 호출 절차 와 마찬가지 로 APP->SYSTEM->APP 호출 순서 입 니 다.
    frameworks/base/core/java/android/view/ViewRootImpl.java
    
    @Override
    public void findAccessibilityNodeInfoByAccessibilityId(long accessibilityNodeId,
     Region interactiveRegion, int interactionId,
     IAccessibilityInteractionConnectionCallback callback, int flags,
     int interrogatingPid, long interrogatingTid, MagnificationSpec spec) {
     ViewRootImpl viewRootImpl = mViewRootImpl.get();
     if (viewRootImpl != null && viewRootImpl.mView != null) {
     //                     
     viewRootImpl.getAccessibilityInteractionController()
      .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityNodeId,
       interactiveRegion, interactionId, callback, flags, interrogatingPid,
       interrogatingTid, spec);
     } else {
     ...
     }
    }
    frameworks/base/core/java/android/view/AccessibilityInteractionController.java
    
    private void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
     ...
     //           
     List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
     infos.clear();
     try {
     if (mViewRootImpl.mView == null || mViewRootImpl.mAttachInfo == null) {
      return;
     }
     mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
     View root = null;
     if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
      root = mViewRootImpl.mView;
     } else {
      root = findViewByAccessibilityId(accessibilityViewId);
     }
     ...
     } finally {
     try {
      ...
      adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
      //   callback binder proxy  ,     binder   
      callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
      infos.clear();
     } catch (RemoteException re) {
      /* ignore - the other side will time out */
     }
     ...
     }
    }
    AccessibilityInteractionClient.setFindAccessibilityNodeInfosResult
    frameworks/base/core/java/android/view/accessibility/AccessibilityInteractionClient.java
    
    public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
      int interactionId) {
     synchronized (mInstanceLock) {
     if (interactionId > mInteractionId) {
      if (infos != null) {
      ...
      //            
      if (!isIpcCall) {
       mFindAccessibilityNodeInfosResult = new ArrayList<>(infos);
      } else {
       mFindAccessibilityNodeInfosResult = infos;
      }
      } else {
      mFindAccessibilityNodeInfosResult = Collections.emptyList();
      }
      mInteractionId = interactionId;
     }
     //    ,    ,        
     mInstanceLock.notifyAll();
     }
    }
    findAccessibility NodeInfosByText 와 permAction 은 목표 노드 를 조작 합 니 다.
    AccessibilityNodeInfo.findAccessibilityNodeInfosByText
    부모 노드 정 보 를 찾 으 면 부모 노드 를 통 해 해당 하 는 하위 노드 정 보 를 얻 을 수 있 습 니 다.
    frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.java
    
    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
     ...
     //      ,  AccessibilityInteractionClient     
     AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
     return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
      text);
    }
    ``` 
    다음 코드 프로 세 스 는 getRootInActiveWindow 와 대체적으로 일치 하 므 로 상세 하 게 분석 하지 않 습 니 다.
    #### AccessibilityNodeInfo.performAction
    대응 하 는 하위 노드 를 가 져 온 후 permAction 을 통 해 해당 하 는 동작 을 수행 할 수 있 습 니 다.예 를 들 어 자주 사용 하 는 클릭 과 같 습 니 다.
    최종 적 으로 Accessibility InteractionController 로 호출 되 었 습 니 다.Accessibility Provier 를 얻 으 면 permaction 의 최종 작업 을 수행 할 수 있 습 니 다.
    frameworks/base/core/java/android/view/AccessibilityInteractionController.java
    
    ```java
    private void performAccessibilityActionUiThread(Message message) {
     View target = null;
     if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
     target = findViewByAccessibilityId(accessibilityViewId);
     } else {
     target = mViewRootImpl.mView;
     }
     if (target != null && isShown(target)) {
     AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
     if (provider != null) {
      if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
      //       performAction  
      succeeded = provider.performAction(virtualDescendantId, action,
       arguments);
      } else {
      succeeded = provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
       action, arguments);
      }
     } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
      succeeded = target.performAccessibilityAction(action, arguments);
     }
     }
    }
    frameworks/base/core/java/android/view/View.java
    
    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
     ...
     switch (action) {
     case AccessibilityNodeInfo.ACTION_CLICK: {
      if (isClickable()) {
      //           View.performClick  
      performClick();
      return true;
      }
     } break;
     ...
    }
    분석 을 통 해 알 수 있 듯 이 Accessibility 서비스 프레임 워 크 는 hook 이 Android View 구성 요소 트 리 에서 의 실현 과 유사 하 다.이것 은 독립 된 메커니즘 이 아니 라'기생'이 View 의 디 스 플레이,이벤트 배포 절차 에 있다.
    총결산
    기능 은 ViewRootImpl,ViewGroup,View 보기 등급 관리 에 의존 하 는 기본 구 조 를 실현 합 니 다.보기 가 변 할 때 이 벤트 를 보 내 고 보기 작업 요청 을 받 았 을 때 도 응답 할 수 있 습 니 다.
    system_server 는 이 기능 을 실현 하 는 과정 에서 중개인 역할 을 하고 있다.감 청 된 앱 의 보기 가 바 뀌 면 앱 이 먼저 system 로 이 벤트 를 보 냅 니 다.server,그 다음 에 감청 자 앱 으로 넘 어 갑 니 다.감청 자 앱 이 보기 동작 을 수행 하려 고 할 때 도 먼저 systemserver 에서 해당 하 는 클 라 이언 트 binder proxy 를 찾 은 다음 해당 인 터 페 이 스 를 감청 된 앱 으로 호출 합 니 다.해당 작업 을 완료 한 후 이미 가 져 온 감청 앱 binder proxy 핸들 을 통 해 해당 감청 클 라 이언 트 에 직접 binder call 합 니 다.
    무장 애 권한 은 매우 중요 하 므 로 남용 해 서 는 안 된다 는 것 을 명심 해 야 한다.앱 자체 도 충분 한 안전 의식 을 가지 고 악성 앱 이 이 서 비 스 를 통 해 사용자 의 프라이버시 정 보 를 얻 는 것 을 방지 해 야 한다.
    자,이상 이 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기