Android app 설치 프로세스 분석(Nougat 기반)

27129 단어
우선 우리는 전체적으로 다음과 같은 몇 가지 설치 상황으로 나눌 수 있음을 분명히 해야 한다
  • 시스템 켜기 응용 프로그램 설치, 시스템 수준의 응용 프로그램 설치, 사용자가 루트 권한을 얻지 못한 상황에서 마운트 해제할 수 없는 응용 프로그램
  • adb 설치 응용 프로그램, 설치 인터페이스 없음
  • 제3자 시장에서 다운로드한 앱은 여기에 상황을 구분해야 한다. 일부는 컴퓨터를 통과한 클라이언트가 설치하지 않은 인터페이스이고 일부는 핸드폰에 설치된 시장이고 설치된 인터페이스가 있다
  • 그러면 우리는 몇 가지 상황에 대해 그것의 설치 절차를 일일이 분석할 것이다
    전원 켜기 설치
  • 시스템 서버는 시스템 서버의main()->init1()->init2()->new ServerThread()->구축 PMS 이상은 시스템Server입니다.cpp의 코드는 이해하면 됩니다.우리는 PMS에 가서 이어지는 논리를 살펴보았다. PMS의 구조 방법에서 우리는 덩어리의 논리를 보았다. 원본 코드가 너무 길기 때문에 우리는 구조 방법에서 다음과 같은 두 가지 방법을 직접 검색했다.
  • scanDirTracedLI   //            ,             
    scanDirLI
    

    이 두 가지 방법을 사용하여 scanDirLI부터 추적하여 설치하는 것입니다.
    private void scanDirLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
            final File[] files = dir.listFiles();
            ...
            for (File file : files) {
                final boolean isPackage = (isApkFile(file) || file.isDirectory())
                        && !PackageInstallerService.isStageName(file.getName());
                if (!isPackage) {
                    // Ignore entries which are not packages
                    continue;
                }
                try {
                    scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                            scanFlags, currentTime, null);
                } catch (PackageManagerException e) {
                    Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
    
                    // Delete invalid userdata apps
                    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                            e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                        logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
                        removeCodePathLI(file);
                    }
                }
            }
        }
    

    계속해서 scanPackageTracedLI 메서드를 살펴보십시오.
    private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
                long currentTime, UserHandle user) throws PackageManagerException {
            if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
            PackageParser pp = new PackageParser();
            pp.setSeparateProcesses(mSeparateProcesses);
            pp.setOnlyCoreApps(mOnlyCore);
            pp.setDisplayMetrics(mMetrics);
    
            if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
                parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
            }
    
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
            final PackageParser.Package pkg;
            try {
                pkg = pp.parsePackage(scanFile, parseFlags);
            } catch (PackageParserException e) {
                throw PackageManagerException.from(e);
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
    
            return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
        }
    

    우리는 이 방법에서부터 Parse Package를 볼 수 있다. Package Parser를 통해 apk 파일을 해석하면 Package Parser의 파일에 가서 이 Parse Package 방법을 볼 수 있다. 바로 설치 패키지의 설정 파일을 해석하고 Package에 저장하여 되돌려주는 것이다. 중간에 엎치락뒤치락하며 설정 파일의 여러 가지 상황에 대해 Parse를 한다.가장 중요한 것은parseBaseApkCommon 방법이다. 이 방법은 manifast 파일의 라벨을 분석하고 그 중에서parseBaseApplication 방법을 호출하여 응용 프로그램 라벨을 분석한다.
    그리고 scanPackageLI->scanPackageDirtyLI, 후자의 방법에서 우리는 이전에 해석한
    PackageParser.Provider p = pkg.providers.get(i);
                    p.info.processName = fixProcessName(pkg.applicationInfo.processName,
                            p.info.processName, pkg.applicationInfo.uid);
                    mProviders.addProvider(p);
    
    PackageParser.Service s = pkg.services.get(i);
                    s.info.processName = fixProcessName(pkg.applicationInfo.processName,
                            s.info.processName, pkg.applicationInfo.uid);
                    mServices.addService(s);
    

    등등, 우리는 해당하는provider, 서비스,receiver,activity를 모두 PMS의 구성원 집합 클래스에 저장할 것이다.
    이 시스템에 적용된 설치는 모두 완성된 셈이다. 아마도 당신은 우리가 어떤 인스타그램의 어떤 방법을 보았는지 말할 수 있을 것이다. 사실 가장 정수적인 것은 위에 있는 마지막 1000+줄 덩어리의 방법이다. 이 안에 있는 모든 Parser가 나온 정보를 PMS에 저장한 것이다. 이것이 바로 설치라는 것이다.설치는 apk의 정보를 분석하여 PMS에 저장하는 과정입니다. 그리고launcher에 아이콘을 생성하여 사용자가 열 수 있도록 합니다. 단지 이것뿐입니다.
    네트워크에서 어플리케이션 설치 다운로드
    최종적으로 다음과 같은 방식으로 설치를 진행하였다
    String fileName = "/mnt/usb/sda4/test.apk";
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive");
    startActivity(intent);
    

    Package Installer Activity를 시작합니다. 이 activity는 안드로이드의 시스템 응용 프로그램 중 하나입니다. 원본 코드는 패키지/app/package installer에서 구체적인 원본 코드는 아래 링크로 볼 수 있습니다.https://github.com/android/platform_packages_apps_packageinstaller/blob/master/src/com/android/packageinstaller/PackageInstallerActivity.java우리는 그 onCreate 방법에서 본 후에 checkIfAllowedAndInitiateInstall->initiateInstall->startInstallConfirm을 호출하여 권한에 관한 문제를 처리할 것입니다
    다음에 아래 ok 단추를 누르면 설치 절차에 들어갑니다.
     public void onClick(View v) {
            if (v == mOk) {
                if (mOkCanInstall || mScrollView == null) {
                    if (mSessionId != -1) {
                        mInstaller.setPermissionsResult(mSessionId, true);
                        clearCachedApkIfNeededAndFinish();
                    } else {
                        startInstall();
                    }
                } else {
                    mScrollView.pageScroll(View.FOCUS_DOWN);
                }
            } else if (v == mCancel) {
               ...
        }
    

    다음으로는 Start Install 방법을 살펴보겠습니다.
    private void startInstall() {
            // Start subactivity to actually install the application
            Intent newIntent = new Intent();
            ...
            newIntent.setClass(this, InstallAppProgress.class);
            ...
            startActivity(newIntent);
            finish();
        }
    

    Install App Progress라는 activity로 넘어가면 우리가 설치한 후에 들어간 설치 인터페이스가 뚜렷하다. 진행 표시줄이 계속 깜빡이는 설치 중인 인터페이스가 있다.onCreate()->initView():
    void initView() {
            setContentView(R.layout.op_progress);
    
            ...
    
            if ("package".equals(mPackageURI.getScheme())) {
                try {
                    pm.installExistingPackage(mAppInfo.packageName);
                    onPackageInstalled(PackageInstaller.STATUS_SUCCESS);
                } catch (PackageManager.NameNotFoundException e) {
                    onPackageInstalled(PackageInstaller.STATUS_FAILURE_INVALID);
                }
            } else {
               ...
    
                mInstallHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        doPackageStage(pm, params);
                    }
                });
            }
        }
    

    좀 간소화하였는데, 주로 이 두 가지 방법이다.
    installExistingPackage
    하나는pm.install Existing Package, 실제로 호출된 것이 Application Package Manager의 install Existing Package라는 것을 알고 있습니다
    
        @Override
        public int installExistingPackage(String packageName) throws NameNotFoundException {
            return installExistingPackageAsUser(packageName, mContext.getUserId());
        }
    
        @Override
        public int installExistingPackageAsUser(String packageName, int userId)
                throws NameNotFoundException {
            try {
                int res = mPM.installExistingPackageAsUser(packageName, userId);
                if (res == INSTALL_FAILED_INVALID_URI) {
                    throw new NameNotFoundException("Package " + packageName + " doesn't exist");
                }
                return res;
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    

    계속해서 우리는 PMS의 install Existing Package Asuser 방법을 찾아갔는데 구체적인 방법은 붙이지 않았다. 실제적인 의미는 이 설치 패키지가 이미 설치되었다는 것이다. 단지 현재 사용자가 설치하지 않았기 때문에 백엔드의 설정을 처리할 것이다. 실제적으로 설치한 물건과 관련이 없다.다른 방법을 보도록 하겠습니다.
    doPackageStage
    private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
            final PackageInstaller packageInstaller = pm.getPackageInstaller();
            PackageInstaller.Session session = null;
            try {
                session = packageInstaller.openSession(sessionId);
                ...
                session.commit(pendingIntent.getIntentSender());
            } catch (IOException e) {
                onPackageInstalled(PackageInstaller.STATUS_FAILURE);
            } finally {
                IoUtils.closeQuietly(session);
            }
        }
    

    그중에서session에 대해 많은 조작을 했습니다. 우리는 가장 중요한 마지막session만 보았습니다.commit,
  • PackageInstaller.Session 이session은 5.0 이후에 새로 추가된 설치 처리 방식이다. 우리는 이를 기록으로 이해할 수 있다. 브라우저의session의 역할과 같다. 이것은 앱에 설치된 모든 정보를 저장하는 데 사용된다. 만약에 우리의 핸드폰이 이상으로 반만 설치하고 종료되면 다음에 다시 핸드폰을 켤 때마지막 설치를 계속할 수 있습니다.

  • OK, 이어서 Package Installer로 들어가겠습니다.javasession의commit방법
    public void commit(@NonNull IntentSender statusReceiver) {
                try {
                    mSession.commit(statusReceiver);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
    

    여기 mSession 저희가 봤을 때는...
            private IPackageInstallerSession mSession;
    
     /** {@hide} */
            public Session(IPackageInstallerSession session) {
                mSession = session;
            }
    

    극악무도한 binder,session의 정의로 돌아가기
    session = packageInstaller.openSession(sessionId);
    

    오픈 세션 방법 찾기
    public @NonNull Session openSession(int sessionId) throws IOException {
            try {
                return new Session(mInstaller.openSession(sessionId));
            } catch (RuntimeException e) {
                ExceptionUtils.maybeUnwrapIOException(e);
                throw e;
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    

    mInstaller란 무엇인지,
        private final IPackageInstaller mInstaller;
    

    또 하나의 binder가 오면 우리는 한 걸음 한 걸음 붙이지 않을 것이다. 독자도 스스로 최종 실현을 찾을 수 있다고 믿는 것은 Package Installer Session이다.
    @Override
        public void commit(IntentSender statusReceiver) {
            ...
            final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
                    statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
            mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
        }
    

    부분을 생략하고 마지막에 Handler한테 메시지를 보냈어요.
            mHandler = new Handler(looper, mHandlerCallback);
    
    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                // Cache package manager data without the lock held
                final PackageInfo pkgInfo = mPm.getPackageInfo(
                        params.appPackageName, PackageManager.GET_SIGNATURES /*flags*/, userId);
                final ApplicationInfo appInfo = mPm.getApplicationInfo(
                        params.appPackageName, 0, userId);
    
                synchronized (mLock) {
                    if (msg.obj != null) {
                        mRemoteObserver = (IPackageInstallObserver2) msg.obj;
                    }
    
                    try {
                        commitLocked(pkgInfo, appInfo);
                    } catch (PackageManagerException e) {
                        final String completeMsg = ExceptionUtils.getCompleteMessage(e);
                        Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
                        destroyInternal();
                        dispatchSessionFinished(e.error, completeMsg, null);
                    }
    
                    return true;
                }
            }
        };
    

    중요한 것은 바로 그commitLocked이다. 이 방법은 비교적 길고 여전히 권한과 설정 방면의 일을 하는데 방법의 마지막에 볼 수 있다.
    mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
                    installerPackageName, installerUid, user, mCertificates);
    

    PMS의 InstallStage 메서드로 이동합니다.
    void installStage(String packageName, File stagedDir, String stagedCid,
                IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
                String installerPackageName, int installerUid, UserHandle user,
                Certificate[][] certificates) {
            ...
            final Message msg = mHandler.obtainMessage(INIT_COPY);
            final InstallParams params = new InstallParams(origin, null, observer,
                    sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
                    verificationInfo, user, sessionParams.abiOverride,
                    sessionParams.grantedRuntimePermissions, certificates);
            ...
            msg.obj = params;
            ...
            mHandler.sendMessage(msg);
        }
    

    우리는 전체 과정을 상기 세 개의 코드로 간소화하여 what를 INIT 로 보낸다COPY 메시지
    case INIT_COPY: {
                        HandlerParams params = (HandlerParams) msg.obj;
                        int idx = mPendingInstalls.size();
                        if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
                        //         ,      false,          mBound   true,         
                        //           else   
                        if (!mBound) {
                            Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                                    System.identityHashCode(mHandler));
                            // If this is the only one pending we might
                            // have to bind to the service again.
                            if (!connectToService()) {
                                Slog.e(TAG, "Failed to bind to media container service");
                                params.serviceError();
                                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                                        System.identityHashCode(mHandler));
                                if (params.traceMethod != null) {
                                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,
                                            params.traceCookie);
                                }
                                return;
                            } else {
                                // Once we bind to the service, the first
                                // pending request will be processed.
                                mPendingInstalls.add(idx, params);
                            }
                        } else {
                            mPendingInstalls.add(idx, params);
                            // Already bound to the service. Just make
                            // sure we trigger off processing the first request.
                            if (idx == 0) {
                                mHandler.sendEmptyMessage(MCS_BOUND);
                            }
                        }
                        break;
                    }
    

    바로 마지막에 MCS를 보내주셨는데...BOUND 메시지
     case MCS_BOUND: {
                        ...
                        //      ,    else if
                        if (mContainerService == null) {
                            ...
                        } 
                        //           0
                        else if (mPendingInstalls.size() > 0) {
                            HandlerParams params = mPendingInstalls.get(0);
                            if (params != null) {
                                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                                        System.identityHashCode(params));
                                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                                //     copy
                                if (params.startCopy()) {
                                    // We are done...  look for more work or to
                                    // go idle.
                                    if (DEBUG_SD_INSTALL) Log.i(TAG,
                                            "Checking for more work or unbind...");
                                    // Delete pending install
                                    if (mPendingInstalls.size() > 0) {
                                        mPendingInstalls.remove(0);
                                    }
                                    //              ,         
                                    //        MCS_BOUND  ,      
                                    if (mPendingInstalls.size() == 0) {
                                        if (mBound) {
                                            if (DEBUG_SD_INSTALL) Log.i(TAG,
                                                    "Posting delayed MCS_UNBIND");
                                            removeMessages(MCS_UNBIND);
                                            Message ubmsg = obtainMessage(MCS_UNBIND);
                                            // Unbind after a little delay, to avoid
                                            // continual thrashing.
                                            sendMessageDelayed(ubmsg, 10000);
                                        }
                                    } else {
                                        // There are more pending requests in queue.
                                        // Just post MCS_BOUND message to trigger processing
                                        // of next pending install.
                                        if (DEBUG_SD_INSTALL) Log.i(TAG,
                                                "Posting MCS_BOUND for next work");
                                        mHandler.sendEmptyMessage(MCS_BOUND);
                                    }
                                }
                                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                            }
                        } else {
                            // Should never happen ideally.
                            Slog.w(TAG, "Empty queue");
                        }
                        break;
                    }
    

    바로 startCopy로 이동합니다.
    final boolean startCopy() {
                boolean res;
                try {
                    if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
                    //              ,     
                    if (++mRetries > MAX_RETRIES) {
                        Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                        mHandler.sendEmptyMessage(MCS_GIVE_UP);
                        handleServiceError();
                        return false;
                    } else {
                        //      
                        handleStartCopy();
                        res = true;
                    }
                } catch (RemoteException e) {
                    if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
                    mHandler.sendEmptyMessage(MCS_RECONNECT);
                    res = false;
                }
                //  return code
                handleReturnCode();
                return res;
            }
    

    먼저handleStartCopy 방법을 살펴보겠습니다. 발견시 추상적인 방법, 구체적인 실현을 보면 인터넷에서 InstallParams를 찾을 수 있습니다.
    public void handleStartCopy() throws RemoteException {
        int ret = PackageManager.INSTALL_SUCCEEDED;
        //            sdcard ,       
        if (origin.staged) {
            if (origin.file != null) {
                installFlags |= PackageManager.INSTALL_INTERNAL;
                installFlags &= ~PackageManager.INSTALL_EXTERNAL;
            } else if (origin.cid != null) {
                installFlags |= PackageManager.INSTALL_EXTERNAL;
                installFlags &= ~PackageManager.INSTALL_INTERNAL;
            } else {
                throw new IllegalStateException("Invalid stage location");
            }
        }
        ...
        //   APK         
        if (onInt && onSd) {
            // Check if both bits are set.
            Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
            ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
        } else if (onSd && ephemeral) {
            Slog.w(TAG,  "Conflicting flags specified for installing ephemeral on external");
            ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
        } else {
            ...
        }
        ...
        // createInstallArgs            
        final InstallArgs args = createInstallArgs(this);
        
        if (ret == PackageManager.INSTALL_SUCCEEDED) {
            ...
                //   InstallArgs copyApk  
                ret = args.copyApk(mContainerService, true);
            }
        }
        mRet = ret;
    }
    

    주요 업무는 apk 메시지를 지정한 위치로 복사하고handle ReturnCode를 보는 것이다
    void handleReturnCode() {
        // If mArgs is null, then MCS couldn't be reached. When it
        // reconnects, it will try again to install. At that point, this
        // will succeed.
        if (mArgs != null) {
            processPendingInstall(mArgs, mRet);
        }
    }
    

    프로세스 Pending Install () ->install Package Traced LI () ->install Package LI () 는 전체 방법이 비교적 길고 주로 목록 파일을 분석하고 인증서를 얻으며 서명을 추출하는 작업을 했습니다. 이 방법에서 다음과 같은 코드 세그먼트를 찾을 수 있습니다. 바로 이 코드가 설치를 처리했습니다.
    
        try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
                "installPackageLI")) {
            if (replace) {
                // 4.       packages
                replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                        installerPackageName, res);
            } else {
                // 5.    packages
                installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                        args.user, installerPackageName, volumeUuid, res);
            }
        }
    

    우리 계속
    /*
         * Install a non-existing package.
         */
        private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
                int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
                PackageInstalledInfo res) {
            ...
            try {
                PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
                        System.currentTimeMillis(), user);
    
                updateSettingsLI(newPackage, installerPackageName, null, res, user);
    
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                    prepareAppDataAfterInstallLIF(newPackage);
    
                } else {
                    // Remove package from internal structures, but keep around any
                    // data that might have already existed
                    deletePackageLIF(pkgName, UserHandle.ALL, false, null,
                            PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
                }
            } catch (PackageManagerException e) {
                res.setError("Package couldn't be installed in " + pkg.codePath, e);
            }
            ...
        }
    

    삭제와 생략을 통해 우리는 우리가 익숙한 방법인 scanPackageTracedLI를 보았고 구체적인 절차는 상기 시스템 응용 설치를 보았다.
    마지막으로 또 다른 ADB의 설치를 보도록 하겠습니다.
    adb 설치
    명령줄 창에adb install을 입력할 때 실제로 시스템은 다음과 같은 프로그램을 실행합니다
    db_commandline
        install_app_legacy or install_app 
            pm_command
                send_shell_command
                    Pm.runInstall()
    

    이 과정은 apk 파일copy를 데이터/local/tmp/디렉터리에 보내고 셸 서비스에pm 명령을 보내서 apk를 설치하고 마지막으로 Pm를 호출합니다.runInstall () 방법으로 apk를 설치합니다.우리는 이 방법을 볼 수 있다.
    private int runInstall() throws RemoteException {
            ...
            final int sessionId = doCreateSession(params.sessionParams,
                    params.installerPackageName, params.userId);
    
            try {
                if (inPath == null && params.sessionParams.sizeBytes == 0) {
                    System.err.println("Error: must either specify a package size or an APK file");
                    return 1;
                }
                if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
                        false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                    return 1;
                }
                if (doCommitSession(sessionId, false /*logSuccess*/)
                        != PackageInstaller.STATUS_SUCCESS) {
                    return 1;
                }
                System.out.println("Success");
                return 0;
            } finally {
                try {
                    mInstaller.abandonSession(sessionId);
                } catch (Exception ignore) {
                }
            }
        }
    

    우리는 여전히 생략하고 보며 위의 이 단락을 생략하고 맨 아래에서 세 가지 중요한 방법을 직접 보았다. 바로doCreate Session,doWrite Session,doCommit Session, 마지막 것을 클릭한다.
    private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
            PackageInstaller.Session session = null;
            try {
                session = new PackageInstaller.Session(
                        mInstaller.openSession(sessionId));
    
                final LocalIntentReceiver receiver = new LocalIntentReceiver();
                session.commit(receiver.getIntentSender());
    
                final Intent result = receiver.getResult();
                final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                        PackageInstaller.STATUS_FAILURE);
                if (status == PackageInstaller.STATUS_SUCCESS) {
                    if (logSuccess) {
                        System.out.println("Success");
                    }
                } else {
                    System.err.println("Failure ["
                            + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
                }
                return status;
            } finally {
                IoUtils.closeQuietly(session);
            }
        }
    

    익숙한 세션을 봤어요.commit인가,
    총결산
    다음은 인터페이스를 설치하는 app 설치 절차가 있고adb 설치를 중간에 삽입하고 인터페이스를 설치하지 않은 시스템 설치입니다.
  • PackageInstallerActivity의onCreate();
  • Intent에서 얻은 데이터의scheme에 따라 서로 다른 처리 논리로 앱을 설치한다.
  • App 파일을 Package 객체로 분석합니다.
  • initInstall() 함수로 apk의 권한 정보를 얻기;
  • onClick()감청 사용자가 설치 동의 버튼을 클릭하는 것을 감청한다.
  • InstallAppProcess Activity를 시작합니다.
  • onCrete() 함수 호출
  • initView() 함수,
  • Session을 위해 DoPackage Stage() 함수를 계속 호출합니다.commit(); adb install 1.Pm.runInstall() 2.Pm.doCommitSession()
  • Handler 메커니즘을 통해 startCopy() 함수를 시작합니다.
  • startCopy ()는handlerStartCopy ()를 시작해서 apk를/data/app, pkg-name/로 복사합니다.so 파일을/data/app/pkg-name/lib/로 복사
  • handlerReturnCode() 함수;
  • handlerReturnCode에서 installPackageLI() 함수를 호출했습니다.
  • installPackageLI()에서 우선collectcertificates() 함수를 호출하여 apk의 서명에 대한 검사를 실시하여 apk가 불법으로 수정되지 않았는지 확인한다(그 중의 파일을 수정한다).그런 다음 collectManifestDigest() 함수를 호출하여 Manifest를 계산했습니다.xml의 파일 요약과 Package의 manfestDigest 구성원이 존재합니다.이어 apk 파일에서 추출한 manfest 계산 요약을 이전 설치 파일에서 분석한 것과 비교하고 일치하지 않으면 이상을 던집니다.그리고 앱이 업그레이드되거나 공유 속성sharedUid를 사용한 경우 설치할 앱의 인증서가 일치하는지 확인하고 업그레이드 방식에 대해 구체적으로 처리했다. 여기서 두 가지 문제가 있는데 그것이 바로 업그레이드 과정에서의 Upgrade keysets 업그레이드 방식과Signature[]의 획득이 명확하지 않다는 것이다.
  • 앱을 업그레이드하느냐 새 앱을 설치하느냐에 따라 서로 다른 함수를 호출하여 처리 절차를 진행한다. 1.scanPackageTracedLI 2.scanPackageLI 3.scanPackageDirtyLI
  • 마지막으로scanPackageDirtyLI 방법에서 모든provider, 서비스,receiver,activity를parser에서 꺼내 PMS에 저장합니다.

  • 이상!

    좋은 웹페이지 즐겨찾기