❤️Android 从源码解读 Apk 的安装过程 ❤️(上)

简介: 目录App安装过程PackageInstallerActivityonCreat()onResume()bindUi()startInstall()InstallInstallingonCreat()onStart()onResume()InstallingAsyncTaskPackageInstaller.commit()PackageInstallerSessioncommit()dispatchStreamValidateAndCommit()mHandler()handleStreamValidateAndCommit()


App安装过程


源码:Android SDK 30


    当我们点击某一个 App 安装包进行安装时,首先会弹出一个系统界面指示我们进行安装操作。这个界面是 Android Framework 中预置的一个 Activity>PackageInstallerActivity.java。


PackageInstallerActivity

       frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java


/**
 * This activity is launched when a new application is installed via side loading
 * The package is first parsed and the user is notified of parse errors via a dialog.
 * If the package is successfully parsed, the user is notified to turn on the install unknown
 * applications setting. A memory check is made at this point and the user is notified of out
 * of memory conditions if any. If the package is already existing on the device,
 * a confirmation dialog (to replace the existing package) is presented to the user.
 * Based on the user response the package is then installed by launching InstallAppConfirm
 * sub activity. All state transitions are handled in this activity
 */
public class PackageInstallerActivity extends AlertActivity {
      ...
}


     看到 Activity 当然是先从 onCreate() 开始了


onCreat()


public class PackageInstallerActivity extends AlertActivity {
    @Override
    protected void onCreate(Bundle icicle) {
        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
        super.onCreate(null);
        if (icicle != null) {
            mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
        }
        //mPm = PackageManagerService
        mPm = getPackageManager();
        //mIpm = IPackageManager
        //将原始接口返回给包管理器。
        mIpm = AppGlobals.getPackageManager();
        //AppOpsManager 涵盖了从帮助运行时权限访问控制和跟踪到电池消耗跟踪的各种功能。
        mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
        //提供在设备上安装、升级和删除应用程序的能力。
        mInstaller = mPm.getPackageInstaller();
        //在多用户系统上管理用户和用户详细信息
        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
        final Intent intent = getIntent();
        ...
        //安装包Uri
        final Uri packageUri;
        if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
            //获取android.content.pm.extra.SESSION_ID
            final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
            //获取安装Session的详细信息
            final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
            if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
                //获取SessionInfo失败退出安装
                finish();
                return;
            }
            mSessionId = sessionId;
            packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
            mOriginatingURI = null;
            mReferrerURI = null;
        } else {
            mSessionId = -1;
            packageUri = intent.getData();
            mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
            mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
        }
        if (packageUri == null) {
            //获取packageUri失败退出安装
            setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
            finish();
            return;
        }
        //解析 Uri 并为此包设置安装程序。
        boolean wasSetUp = processPackageUri(packageUri);
        if (!wasSetUp) {
            return;
        }
    }
}


 在 onCreate() 方法中,首先执行一些初始化操作,获取PackageManager和PackageInstaller、UserManager等对象,根据当前Intent的信息最一些逻辑判断并弹出消息弹窗,然后获取一些安装包信息。接下来咱们安装 Activity 的生命周期往下看 onStart() 没有重写,咱继续往下看


onResume()


    @Override
    protected void onResume() {
        super.onResume();
        //processPackageUri已拿到mAppSnippet
        if (mAppSnippet != null) {
            // 在禁用确定按钮的情况下加载虚拟布局,直到我们覆盖此布局
            // 开始安装确认
            bindUi();
            // 检查是否允许安装包,如果允许则启动安装。 
            //如果不允许显示适当的对话框。
            checkIfAllowedAndInitiateInstall();
        }
        if (mOk != null) {
            mOk.setEnabled(mEnableOk);
        }
    }


bindUi()


    private void bindUi() {
        ...
        mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
                (ignored, ignored2) -> {
                    if (mOk.isEnabled()) {
                        ...
                            startInstall();
                        ...
                    }
                }, null);
        ...
    }


这里点击安装按钮进去 startInstall() 方法,失败的情况咱就不看了,继续往下走着。


startInstall()


    private void startInstall() {
        // 启动子Activity,安装应用程序
        Intent newIntent = new Intent();
        newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
                mPkgInfo.applicationInfo);
        newIntent.setData(mPackageURI);
        newIntent.setClass(this, InstallInstalling.class);
        ...
        newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
        startActivity(newIntent);
        finish();
    }


startInstall 调用即使启用了一个新的Activity–>InstallInstalling。会把安装包的信息通过 Intent 传递到 InstallInstalling 这个 Activity。


InstallInstalling

       frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java


/**
 * Send package to the package manager and handle results from package manager. Once the
 * installation succeeds, start {@link InstallSuccess} or {@link InstallFailed}.
 * <p>This has two phases: First send the data to the package manager, then wait until the package
 * manager processed the result.</p>
 */
public class InstallInstalling extends AlertActivity {
      ...
}


在这个页面点击安装时,InstallInstalling 的作用主要是向 PMS 发送包信息以及处理回调。 这有两个阶段:


  • 首先将数据发送给 PackageManager ;


  • 等待 PackageManager 处理结果(InstallSuccess 或 InstallFailed)。


       看到 Activity 当然是先从 onCreate() 开始了


onCreat()


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ApplicationInfo appInfo = getIntent()
                .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        //要安装的包的 URI
        mPackageURI = getIntent().getData();
        if ("package".equals(mPackageURI.getScheme())) {
            try {
                //注释1:安装现有包
                getPackageManager().installExistingPackage(appInfo.packageName);
                //安装成功页面
                launchSuccess();
            } catch (PackageManager.NameNotFoundException e) {
                //安装失败页面
                launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
            }
        } else {
             //注释2:没有现有包
             //根据 mPackageURI 创建一个对应的 File
            final File sourceFile = new File(mPackageURI.getPath());
            //显示应用信息 icon,应用名
            PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
            mAlert.setIcon(as.icon);
            ...
            if (savedInstanceState != null) {
                //咱们第一次仅savedInstanceState==null
                //安装包的会话 ID
                mSessionId = savedInstanceState.getInt(SESSION_ID);
                //我们等待的安装事件的 ID
                mInstallId = savedInstanceState.getInt(INSTALL_ID);
                //重新登记结果;如果结果是在 Activtiy 被销毁时交付的,则可能会立即回调
                try {
                    //添加一个观察者。如果此id已存在事件,请在此调用内回调。
                    // 向 InstallEventReceiver 注册一个观察者
                    // InstallEventReceiver 是一个 BroadcastReceiver,可以通过 EventResultPersister 接收到所有的安装事件
                    // 这里事件会回调给 this::launchFinishBasedOnResult
                    InstallEventReceiver.addObserver(this, mInstallId,
                            this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    // Does not happen
                }
            } else {
                //咱们第一次仅savedInstanceState==null
                //创建 SessionParams,它用来携带会话的参数
                PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
                        PackageInstaller.SessionParams.MODE_FULL_INSTALL);
                params.setInstallAsInstantApp(false);
                ...
                params.setInstallReason(PackageManager.INSTALL_REASON_USER);
                File file = new File(mPackageURI.getPath());
                try {
                    // 对 APK 进行轻量级的解析,并将解析的结果赋值给 SessionParams 相关字段 
                    PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
                    params.setAppPackageName(pkg.packageName);
                    params.setInstallLocation(pkg.installLocation);
                    params.setSize(
                            PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
                } catch (PackageParser.PackageParserException e) {
                    ...
                } catch (IOException e) {
                    ...
                }
                try {
                    // 向 InstallEventReceiver 注册一个观察者返回一个新的 mInstallId
                    // InstallEventReceiver 是一个 BroadcastReceiver,可以通过 EventResultPersister 接收到所有的安装事件
                    // 这里事件会回调给 this::launchFinishBasedOnResult
                    mInstallId = InstallEventReceiver
                            .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                                    this::launchFinishBasedOnResult);
                } catch (EventResultPersister.OutOfIdsException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
                try {
                    //PackageInstaller 的 createSession 
                    //方法内部会通过 IPackageInstaller 与 PackageInstallerService进行进程间通信,
                    //最终调用的是 PackageInstallerService 的 createSession 方法来创建并返回 mSessionId
                    mSessionId = getPackageManager().getPackageInstaller().createSession(params);
                } catch (IOException e) {
                    launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
                }
            }
            mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
            //监听会话的更改并更新进度条
            mSessionCallback = new InstallSessionCallback();
        }
    }


NameNotFoundException:当找不到给定的包、应用程序或组件名称时,将引发此异常。


       在 onCreate() 方法中,首先执行一些初始化操作,比如获取 mInstallId、mSessionId以及监听回调 mSessionCallback 。接下来看看 onStart() 。


onStart()


    @Override
    protected void onStart() {
        super.onStart();
        //设置回话监听回调
        getPackageManager().getPackageInstaller().registerSessionCallback(mSessionCallback);
    }


onResume()


    @Override
    protected void onResume() {
        super.onResume();
        //This is the first onResume in a single life of the activity
        if (mInstallingTask == null) {
            //PackageInstaller:提供在设备上安装、升级和删除应用程序的能力。
            //这包括支持打包为单个“整体”APK 的应用程序或打包为多个“拆分”APK 的应用程序。
            PackageInstaller installer = getPackageManager().getPackageInstaller();
            //安装Session的详细信息
            PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
            if (sessionInfo != null && !sessionInfo.isActive()) {
                //创建mInstallingTask并启动
                mInstallingTask = new InstallingAsyncTask();
                mInstallingTask.execute();
            } else {
                // 安装完成后我们会收到广播
                //mCancelButton不允许点击
                mCancelButton.setEnabled(false);
                //Activity.setFinishOnTouchOutside():设置此活动在其窗口边界外触摸
                setFinishOnTouchOutside(false);
            }
        }
    }


InstallingAsyncTask


    /**
     * Send the package to the package installer and then register a event result observer that
     * will call {@link #launchFinishBasedOnResult(int, int, String)}
     */
    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
            PackageInstaller.Session> {
        volatile boolean isDone;
        @Override
        protected PackageInstaller.Session doInBackground(Void... params) {
            PackageInstaller.Session session;
            try {
                //打开现有Session
                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {
                synchronized (this) {
                    isDone = true;
                    notifyAll();
                }
                return null;
            }
            //设置暂存此会话的当前进度(0-1z之间)。
            session.setStagingProgress(0);
            try {
                File file = new File(mPackageURI.getPath());
                //输入流in
                try (InputStream in = new FileInputStream(file)) {
                    long sizeBytes = file.length();
                    //打开一个流以将 APK 文件写入会话。
                    try (OutputStream out = session
                            .openWrite("PackageInstaller", 0, sizeBytes)) {
                        byte[] buffer = new byte[1024 * 1024];
                        //写入文件
                        while (true) {
                            int numRead = in.read(buffer);
                            if (numRead == -1) {
                                session.fsync(out);
                                break;
                            }
                            if (isCancelled()) {
                                session.close();
                                break;
                            }
                            //将 APK 文件通过 IO 流的形式写入到 PackageInstaller.Session 中
                            out.write(buffer, 0, numRead);
                            if (sizeBytes > 0) {
                                float fraction = ((float) numRead / (float) sizeBytes);
                                session.addProgress(fraction);
                            }
                        }
                    }
                }
                return session;
            } catch (IOException | SecurityException e) {
                Log.e(LOG_TAG, "Could not write package", e);
                session.close();
                return null;
            } finally {
                synchronized (this) {
                    isDone = true;
                    notifyAll();
                }
            }
        }
        @Override
        protected void onPostExecute(PackageInstaller.Session session) {
            if (session != null) {
                //包已下载好,设置安装提交广播
                Intent broadcastIntent = new Intent(BROADCAST_ACTION);
                broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                broadcastIntent.setPackage(getPackageName());
                broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
                PendingIntent pendingIntent = PendingIntent.getBroadcast(
                        InstallInstalling.this,
                        mInstallId,
                        broadcastIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT);
                // 调用 PackageInstaller.Session 的 commit 方法,进行安装
                session.commit(pendingIntent.getIntentSender());
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            } else {
                getPackageManager().getPackageInstaller().abandonSession(mSessionId);
                if (!isCancelled()) {
                    launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
                }
            }
        }
    }


其他没啥好看的,咱么看看具体的session.commit


PackageInstaller.commit()


        public void commit(@NonNull IntentSender statusReceiver) {
            try {
                mSession.commit(statusReceiver, false);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }


mSession 就是 IPackageInstallerSession 。这说明要通过 IPackageInstallerSession 来进行进程间的通信,最终会调用PackageInstallerSession 的 commit 方法


PackageInstallerSession

       frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java


public class PackageInstallerSession extends IPackageInstallerSession.Stub {
      ...
}


commit()


    @Override
    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        if (hasParentSessionId()) {
            throw new IllegalStateException(
                    "Session " + sessionId + " is a child of multi-package session "
                            + mParentSessionId +  " and may not be committed directly.");
        }
        if (!markAsSealed(statusReceiver, forTransfer)) {
            return;
        }
        //return true 多个包
        if (isMultiPackage()) {
            ...
        }
        dispatchStreamValidateAndCommit();
    }


dispatchStreamValidateAndCommit()


    private void dispatchStreamValidateAndCommit() {
        // 通过 Handler 处理消息事件
        mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
    }


mHandler()


    Handler mHandler = new Handler(looper, mHandlerCallback);
    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_STREAM_VALIDATE_AND_COMMIT:
                    handleStreamValidateAndCommit();
                    break;
                ...
            }
            return true;
        }
    };


handleStreamValidateAndCommit()


    private void handleStreamValidateAndCommit() {
        PackageManagerException unrecoverableFailure = null;
        // 这将跟踪会话和任何子项是否已通过验证并准备好进入下一安装阶段
        boolean allSessionsReady = false;
        try {
            allSessionsReady = streamValidateAndCommit();
        } catch (PackageManagerException e) {
            unrecoverableFailure = e;
        }
        //多包,不是多包直接过
        if (isMultiPackage()) {
            ....
        }
        if (!allSessionsReady) {
            return;
        }
        // 通过 Handler 处理MSG_INSTALL消息事件
        mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
    }


mHandler()


    Handler mHandler = new Handler(looper, mHandlerCallback);
    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                ...
                case MSG_INSTALL:
                    handleInstall();
                    break;
                ...
            }
            return true;
        }
    };


handleInstall()


    private void handleInstall() {
        ...
        //return true 会话被再次启用。
        if (params.isStaged) {
            mStagingManager.commitSession(this);
            destroyInternal();
            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
            return;
        }
        //安装 APEX 包,则返回 true。
        if (isApexInstallation()) {
            destroyInternal();
            dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
                    "APEX packages can only be installed using staged sessions.", null);
            return;
        }
        // 对于 multiPackage 会话,读取锁外的子会话,
        // 因为读取持有锁的子会话可能会导致死锁 (b/123391593)。
        List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
        try {
            synchronized (mLock) {
                //接下来咱们就看看这个方法。
                installNonStagedLocked(childSessions);
            }
        } catch (PackageManagerException e) {
            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
            Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
            destroyInternal();
            dispatchSessionFinished(e.error, completeMsg, null);
        }
    }


installNonStagedLocked()


    @GuardedBy("mLock")
    private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
            throws PackageManagerException {
        final PackageManagerService.ActiveInstallSession installingSession =
                makeSessionActiveLocked();
        if (installingSession == null) {
            return;
        }
        //return true 多个包
        if (isMultiPackage()) {
            List<PackageManagerService.ActiveInstallSession> installingChildSessions =
                    new ArrayList<>(childSessions.size());
            ...
            mPm.installStage(installingChildSessions);
        } else {
            mPm.installStage(installingSession);
        }
    }


这里不管不管怎么样都会调用 mPm.installStage(installingChildSessions);


     mPm 就是系统服务 PackageManagerService。


 

 


相关文章
|
3月前
|
Ubuntu 开发工具 Android开发
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
本文介绍了在基于Ubuntu 22.04的环境下配置Python 3.9、安装repo工具、下载和同步AOSP源码包以及处理repo同步错误的详细步骤。
232 0
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
|
3月前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
126 1
|
3月前
|
Android开发 Docker 容器
docker中编译android aosp源码,出现Build sandboxing disabled due to nsjail error
在使用Docker编译Android AOSP源码时,如果遇到"Build sandboxing disabled due to nsjail error"的错误,可以通过在docker run命令中添加`--privileged`参数来解决权限不足的问题。
668 1
|
3月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
453 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
3月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
138 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
3月前
|
Android开发
我的Android 进阶修炼(1): AOSP源码根目录结构
本文介绍了AOSP源码的根目录结构,提供了基于MTK9269 Android 9.0源码的目录说明,帮助读者了解AOSP源码的组织方式和各目录的功能。
184 0
我的Android 进阶修炼(1): AOSP源码根目录结构
|
3月前
|
API 开发工具 Android开发
Android源码下载
Android源码下载
476 0
|
3月前
|
Android开发
解决android apk安装后出现2个相同的应用图标
解决android apk安装后出现2个相同的应用图标
343 2
|
3月前
|
开发工具 Android开发 git
全志H713 Android 11 :给AOSP源码,新增一个Product
本文介绍了在全志H713 Android 11平台上新增名为myboard的产品的步骤,包括创建新的device目录、编辑配置文件、新增内核配置、记录差异列表以及编译kernel和Android系统的详细过程。
113 0
|
3月前
|
Ubuntu 开发工具 Android开发
Repo下载、编译AOSP源码:基于Ubuntu 21.04,android-12.1.0_r27
文章记录了作者在Ubuntu 21.04服务器上配置环境、下载并编译基于Android 12.1.0_r27版本的AOSP源码的过程,包括解决编译过程中遇到的问题和错误处理方法。
189 0
下一篇
无影云桌面