Hi,我是小余。 本文已收录到GitHub Androider-Planet中。这儿有 Android 进阶生长常识体系,关注大众号 [小余的自习室] ,在成功的路上不迷路!

前言

前面一篇文章咱们解说了PKMS的发动进程。

PKMS发动进程中首要做了以下事情:

  • 1.会对某些配置文件进行解析扫描,放到PKMS方针内存中
  • 2.会对体系中的运用包括:overlay,system,vendor,app等途径下的运用进行扫描,假如发现有版别更新,则进行运用更新操作。
  • 3.初始化保证理进程中需求运用到一些环境方针等。

接下面咱们再来解说下第三方运用的装置进程

运用装置进程

运用装置的办法有如下几种:

1.普通装置办法

在7.0之后,为了进一步提高文件读写的安全性,Android结构履行的StrictMode API方针禁止在您的运用外部揭露file://URI。 假如一项包括文件URI的intent离开您的运用,则运用呈现故障,并呈现FileUriExposedException反常。

这个时分需求运用FileProvider来授权外部文件读写权限

FileProvider

详细运用办法如下:

  • 1.在AndroidManifest文件中定义:

    <provider
        android:authorities="${applicationId}.fileprovider"
        android:name="androidx.core.content.FileProvider"
        android:exported="false"
        android:grantUriPermissions="true"
        >
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/update_files"
            />
    </provider>
    
  • 2.在xml中定义文件update_files.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
      <external-path
        name="external_storage_install"
        path="yuhb/install"/>
    </paths>
    
  • 3.在代码中调用

    /**
    ​
    普通运用装置办法
    7.0今后需求运用FileProvider进行恳求
    @param apkFile
    @param context
    */
    public static void generateInstall(File apkFile, Context context){
       if(!apkFile.exists()){
         return;
       }
       Intent intent = getInstallIntent(apkFile, context);
       context.startActivity(intent);
    }
    //获取装置运用的Intent
    private static Intent getInstallIntent(File apkFile, Context context) {
       Uri data;
       Intent intent = new Intent(Intent.ACTION_VIEW);
       intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
       data = getInstallUri(context,apkFile);
       //7.0今后运用FileProvider处理
       if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//授权其他运用的读权限
         intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);//防止app加固下呈现授权失利状况
       //       intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);//授权其他运用写权限
       }
       intent.setDataAndType(data,"application/vnd.android.package-archive");
       return intent;
    }
    //获取装置文件的uri
    private static Uri getInstallUri(Context context,File apkFile) {
       Uri data;
       //7.0今后运用FileProvider处理
       if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
         data = FileProvider.getUriForFile(context,context.getPackageName()+".fileprovider",apkFile);
       }else {
         data = Uri.fromFile(apkFile);
       }
       return data;
    }
    

2.静默装置办法(需求有root权限)

你是不是尝试了N种办法,打了N个debug,然后得到的却是各式各样的装置失利 ~ 首要类似静默功用一般是被体系所禁止的,只要厂商在自已平台才会开发权限(比如小米的体系运用,默许完结了静默功用,可是假如小米运用移植到vivo就无效了)。

详细运用办法如下:

/**静默装置办法,一般需求root权限或许是厂商自己的体系运用。
@param context
@param apkFilePath
*/
public static void silenceInstallApk(Context context,String apkFilePath) {
   /*apkFilePath:这儿咱们首要传入的是装置包的途径  installObserver:自定义装置的回调,不需求能够删了*/
   File apkFile = new File(apkFilePath);
   //判别途径下的文件是否存在
   if (!apkFile.exists()) {
     Log.e(TAG, "apkFile is null...");
     return;
   }
   String packageName = "";
   //获取装置包的信息
   PackageInfo packageInfo = context.getPackageManager().getPackageArchiveInfo(apkFilePath,
       PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
   if (packageInfo != null) {
     packageName = packageInfo.packageName;
     String versionName = packageInfo.versionName;
   }
   //获取packageInstaller,后面用来创立PackageInstaller.Session
   PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
   //获取创立PackageInstaller.Session的参数
   PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
       PackageInstaller.SessionParams.MODE_FULL_INSTALL);
   /*指示将在此会话中交付的一切APK的总巨细(以字节为单位),体系能够运用它来确保在持续之前存在足够的磁盘空间,
   或许估计装置在外部存储上的容器巨细*/
   sessionParams.setSize(apkFile.length());
   PackageInstaller.Session session = null;
   try {
     //代表一个session的仅有ID,这儿我是在全局变量中声明,因为后面的别的一个办法用到了这个sessionId
     int mSessionId = packageInstaller.createSession(sessionParams);
     if (mSessionId != -1) {
       //也便是在这个外部的onTransfesApkFile()办法中,将会用到sessionId
       boolean copySuccess = onTransfesApkFile(mSessionId,context,apkFilePath, packageName);
       if (copySuccess) {
         session = context.getPackageManager().getPackageInstaller().openSession(mSessionId);
         //设置装置完结后需求发送的一个自定义装置成果广播,这儿我设置了App的NAME,VERSION,PACKAGE
         Intent intent = new Intent(context,
             InstallResultReceiver.class);
         intent.setAction(PackageInstaller.EXTRA_STATUS);
         intent.putExtra("APP_VERSION", "1.0");
         intent.putExtra("APP_PACKAGE", "com.allinpay.manager");
         //履行完毕后,发送intent
         PendingIntent pendingIntent = PendingIntent.getBroadcast(context,1,
             intent, PendingIntent.FLAG_UPDATE_CURRENT);
         //这儿终究进行session的提交
         session.commit(pendingIntent.getIntentSender());
       } else {
         //此处是装置失利的回调,不需求能够删去
   //           if (installObserver != null) {
   //             installObserver.observer(false, apkFilePath, packageName);
   //           }
       }
     }
   } catch (Exception exception) {
     Log.e(TAG, "installApk exception = " + exception.getLocalizedMessage());
   } finally {
     if (null != session) {
       session.close();
     }
     //装置完结需求删去文件
     if (apkFile != null && apkFile.exists()) {
   //         apkFile.delete();
     }
   }
}
​
private static boolean onTransfesApkFile(int mSessionId,Context context,String apkFilePath, String packageName) {
    InputStream in = null;
    OutputStream out = null;
    PackageInstaller.Session session = null;
    boolean success = false;
    try {
        File apkFile = new File(apkFilePath);
        //依据sessionId来获取其代表的session
        session = context.getPackageManager().getPackageInstaller().openSession(mSessionId);
        //向session中写入文件数据
        out = session.openWrite(packageName + "_base.apk", 0, apkFile.length());
        in = new FileInputStream(apkFile);
        int total = 0;
        int len;
        byte[] buffer = new byte[1024];
        while ((len = in.read(buffer)) != -1) {
            total += len;
            out.write(buffer, 0, len);
        }
        session.fsync(out);
        success = true;
    } catch (IOException exception) {
        exception.printStackTrace();
    } finally {
        if (null != session) {
            session.close();
        }
        try {
            if (null != out) {
                out.close();
            }
            if (null != in) {
                in.close();
            }
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }
    return success;
}

这儿咱们来剖析下办法一(普通运用更新办法

  • 1.提取要害代码:调用的startActivity中的Intent特点

    • Action:Intent.ACTION_VIEW
    • Flag:Intent.FLAG_ACTIVITY_NEW_TASK
    • Uri:content格局
    • Type:application/vnd.android.package-archive
  • 2.依据以上Intent,调用了startActivity,然后经过PKMS找到对应的Activity。 终究定位到:InstallStart类,这个类便是发动装置时翻开的第一个Activity

    /**
    Select which activity is the first visible activity of the installation and forward the intent to it.
    */
    public class InstallStart extends Activity {
       protected void onCreate(@Nullable Bundle savedInstanceState) {
         ...
         Uri packageUri = intent.getData();
         //假如Scheme是Content格局
         if (packageUri != null && packageUri.getScheme().equals(
             ContentResolver.SCHEME_CONTENT)) {
           // [IMPORTANT] This path is deprecated, but should still work. Only necessary
           // features should be added.
           // Copy file to prevent it from being changed underneath this process
           nextActivity.setClass(this, InstallStaging.class);
       //假如Scheme是package格局
       } else if (packageUri != null && packageUri.getScheme().equals(
           PackageInstallerActivity.SCHEME_PACKAGE)) {
         nextActivity.setClass(this, PackageInstallerActivity.class);
       //假如Scheme是其他格局
       } else {
         Intent result = new Intent();
         result.putExtra(Intent.EXTRA_INSTALL_RESULT,
             PackageManager.INSTALL_FAILED_INVALID_URI);
         setResult(RESULT_FIRST_USER, result);
    ​
         nextActivity = null;
       }
    ​
       if (nextActivity != null) {
         startActivity(nextActivity);
       }
       finish();
       ```
    }
    

    }

    ​
    ​
    

InstallStart的onCreate办法会对传入的Scheme格局进行判别,然后发动别的一个Activity,并完毕自己。 咱们重点来看Content格局的Activity。终究发动的是InstallStaging.class。看父类名字应该是一个挑选框类型的Activity。

//frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
public class InstallStaging extends AlertActivity {
    protected void onResume() {
        ...
        mStagingTask = new StagingAsyncTask();
        //履行StagingAsyncTask
     mStagingTask.execute(getIntent().getData());
    }
​
    private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
        @Override
        protected Boolean doInBackground(Uri... params) {
            if (params == null || params.length <= 0) {
                return false;
            }
            Uri packageUri = params[0];
            try (InputStream in = getContentResolver().openInputStream(packageUri)) {
                // Despite the comments in ContentResolver#openInputStream the returned stream can
                // be null.
                if (in == null) {
                    return false;
                }
                try (OutputStream out = new FileOutputStream(mStagedFile)) {
                    byte[] buffer = new byte[1024 * 1024];
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) >= 0) {
                        // Be nice and respond to a cancellation
                        if (isCancelled()) {
                            return false;
                        }
                        out.write(buffer, 0, bytesRead);
                    }
                }
            } catch (IOException | SecurityException | IllegalStateException e) {
                Log.w(LOG_TAG, "Error staging apk from content URI", e);
                return false;
            }
            return true;
        }
        @Override
        protected void onPostExecute(Boolean success) {
            if (success) {
                // Now start the installation again from a file
                Intent installIntent = new Intent(getIntent());
                installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
                installIntent.setData(Uri.fromFile(mStagedFile));
                if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
                    installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
                }
                installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
                startActivity(installIntent);
                InstallStaging.this.finish();
            } else {
                showError();
            }
        }
    }
​
}
  • 1.InstallStaging的onResume时会启用了一个ASyncTask,在后台读取apk文件,并写入到mStagedFile文件中。 mStagedFile文件的作用是临时文件,防止在装置进程中对原文件变更
  • 2.在文件读取完结后,调用AsyncTask的onPostExecute办法,这个办法中会再次发动一个DeleteStagedFileOnResult类Activity。

持续进入DeleteStagedFileOnResult

/**
​
 * Trampoline activity. Calls PackageInstallerActivity and deletes staged install file onResult.
  */
  public class DeleteStagedFileOnResult extends Activity {
  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
​
    if (savedInstanceState == null) {
      Intent installIntent = new Intent(getIntent());
      installIntent.setClass(this, PackageInstallerActivity.class);
    
      installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
      startActivityForResult(installIntent, 0);
    }
​
}

看谷歌给咱们的注解:这个类是一个过渡Activity:终究是用来发动PackageInstallerActivity并删去mStagedFile临时文件,这在onCreate办法中也能够看出。

那就转到PackageInstallerActivity,在PackageInstallerActivity中会让引导用户点击装置按钮,点击之后会调用startInstall办法进行装置操作。

//frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
private void startInstall() {
    // Start subactivity to actually install the application
    Intent newIntent = new Intent();
    newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
            mPkgInfo.applicationInfo);
    newIntent.setData(mPackageURI);
    newIntent.setClass(this, InstallInstalling.class);
    String installerPackageName = getIntent().getStringExtra(
            Intent.EXTRA_INSTALLER_PACKAGE_NAME);
    if (mOriginatingURI != null) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
    }
    if (mReferrerURI != null) {
        newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
    }
    if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
        newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
    }
    if (installerPackageName != null) {
        newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
                installerPackageName);
    }
    if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
        newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
    }
    newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
    if (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
    startActivity(newIntent);
    finish();
}

startInstall重新发动了一个“InstallInstalling”去装置,并将发动运用需求的参数信息放到Intent中。

//frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.javapublic class InstallInstalling extends AlertActivity {
    @Override
   protected void onCreate(@Nullable Bundle savedInstanceState) {
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
             PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        ...
        //注册一个装置成果监听器launchFinishBasedOnResult
        mInstallId = InstallEventReceiver
               .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
                   this::launchFinishBasedOnResult);
        ...
        //创立一个createSession
        mSessionId = getPackageManager().getPackageInstaller().createSession(params);
    }
​
    @Override
    protected void onResume() {
        //发动一个InstallingAsyncTask
        mInstallingTask = new InstallingAsyncTask();
       mInstallingTask.execute();
    }
    private final class InstallingAsyncTask extends AsyncTask<Void, Void,
        PackageInstaller.Session> {
        @Override
        protected PackageInstaller.Session doInBackground(Void... params) {
            PackageInstaller.Session session;
            try {
                //翻开Session
                session = getPackageManager().getPackageInstaller().openSession(mSessionId);
            } catch (IOException e) {
            }
            //设置session装置进度
            session.setStagingProgress(0);
            try {
                File file = new File(mPackageURI.getPath());
                try (InputStream in = new FileInputStream(file)) {
                    long sizeBytes = file.length();
                    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;
                            }
                            out.write(buffer, 0, numRead);
                            if (sizeBytes > 0) {
                                float fraction = ((float) numRead / (float) sizeBytes);
                                session.addProgress(fraction);
                            }
                        }
                    }
                }
                return session;
            } catch (IOException | SecurityException e) {
            }
        }
        @Override
        protected void onPostExecute(PackageInstaller.Session session) {
            if (session != null) {
                //注册一个broadcastIntent监听装置成果:BROADCAST_ACTION = "com.android.packageinstaller.ACTION_INSTALL_COMMIT";
                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 | PendingIntent.FLAG_MUTABLE);
                //调用commit进行装置
                session.commit(pendingIntent.getIntentSender());
                mCancelButton.setEnabled(false);
                setFinishOnTouchOutside(false);
            } 
        }
    }
​
}
}

InstallInstalling能够总结为下面几个进程:

  • 1.创立session
  • 2.翻开session
  • 3.copy apk文件到Session中
  • 4.调用commit进行装置。

仔细观察你会发现:这儿进程和咱们前面剖析的静默装置办法进程其实是一样的。而咱们的InstallInstalling是运行在体系进程中,所以具有静默装置权限, 而第三方运用是没有这个权限的、

下面咱们深入PackageInstaller看看其是怎么完结装置进程的?

首要来看context.getPackageManager().getPackageInstaller()获取到的是哪个类?

假如你对Activity了解的话,应该知道context的完结类是ContextImpl类。

定位到它的getPackageManager。

//frameworks/base/core/java/android/app/ContextImpl.java
public PackageManager getPackageManager() {
    if (mPackageManager != null) {
        return mPackageManager;
    }
​
    final IPackageManager pm = ActivityThread.getPackageManager();
    if (pm != null) {
        // Doesn't matter if we make more than one instance.
        return (mPackageManager = new ApplicationPackageManager(this, pm));
    }
    return null;
​
}

由此可知getPackageManager回来的是一个ApplicationPackageManager,而这儿有个要害参数pm,后期操作实践都是经过pm进行的。 pm是经过ActivityThread.getPackageManager()获取。

//frameworks/base/core/java/android/app/ActivityThread.java
public static IPackageManager getPackageManager() {
    if (sPackageManager != null) {
        return sPackageManager;
    }
    final IBinder b = ServiceManager.getService("package");
    sPackageManager = IPackageManager.Stub.asInterface(b);
    return sPackageManager;
}

哦?原来便是获取ServiceManager中的package服务。假如你还有印象,前面咱们剖析过在PKMS的main办法中有下面这段代码。

//构造IPackageManagerImpl方针并将其add到ServiceManager中:name为package
IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();
ServiceManager.addService("package", iPackageManager);

所以这儿回来的是一个IPackageManagerImpl方针。

好了,回到前面context.getPackageManager().getPackageInstaller()

context.getPackageManager():对应ApplicationPackageManager(context,IPackageManagerImpl)

进入ApplicationPackageManager的getPackageInstaller:

//frameworks/base/core/java/android/app/ApplicationPackageManager.java
public PackageInstaller getPackageInstaller() {
    if (mInstaller == null) {
        try {
            mInstaller = new PackageInstaller(mPM.getPackageInstaller(),
                    mContext.getPackageName(), mContext.getAttributionTag(), getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    return mInstaller;
}

由此可知getPackageInstaller回来的是一个PackageInstaller方针,而要害看第一个参数mPM.getPackageInstaller(),这个参数也是实践进行装置的类。

前面剖析过mPM是IPackageManagerImpl方针,进入IPackageManagerImpl.getPackageInstaller()

在IPackageManagerImpl父类IPackageManagerBase完结了getPackageInstaller

//frameworks/base/services/core/java/com/android/server/pm/IPackageManagerBase.java
public final IPackageInstaller getPackageInstaller() {
    ...
    return mInstallerService;
}

回来的是一个mInstallerService,这个mInstallerService是在哪里赋值的呢?经过几轮跳转,定位到。

mInstallerService是在PKMS的构造办法中赋值的:mInstallerService = mInjector.getPackageInstallerService();

mInjector是PackageManagerServiceInjector类(PKMS的运行时环境类)。

终究获取的mInstallerService是在PKMS构造进程中也便是体系开机时初始化的PackageInstallerService方针

好了这儿画了一张图来表示他们之间的关系:

基于Android T:包管理机制详解(下)

由以上剖析可知:context.getPackageManager().getPackageInstaller()获取的是PackageInstaller,而实践装置操作是PackageInstaller的内部PackageInstallerService方针

下面咱们依据前面剖分出的装置进程进行详细剖析 1.创立session 2.翻开session 3.copy apk文件到Session中 4.调用commit进行装置。

1.创立session

//frameworks/base/core/java/android/content/pm/PackageInstaller.java
public int createSession(@NonNull SessionParams params) throws IOException {
    //mInstaller为PackageInstallerService
    return mInstaller.createSession(params, mInstallerPackageName, mAttributionTag,
            mUserId);
}
PackageInstallerService的createSession办法会调用内置的createSessionInternal办法
private int createSessionInternal(SessionParams params, String installerPackageName...){
    //前面一大堆对Session创立的条件进行判别,不满意创立则抛出反常
    //创立随机数的sessionId
    sessionId = allocateSessionIdLocked();
    //创立SessionDir
    stageDir = buildSessionDir(sessionId, params);
    //InstallSource持有运用装置的apk源文件信息
    InstallSource installSource = InstallSource.create(installerPackageName,
         originatingPackageName, requestedInstallerPackageName,
         installerAttributionTag, params.packageSource);
    //创立session : PackageInstallerSession
    session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
            mSilentUpdatePolicy, mInstallThread.getLooper(), mStagingManager, sessionId,
            userId, callingUid, installSource, params, createdMillis, 0L, stageDir, stageCid,
            null, null, false, false, false, false, null, SessionInfo.INVALID_ID,
            false, false, false, PackageManager.INSTALL_UNKNOWN, "");
    //将session放入到mSessions键值对中,key为sessionId
    synchronized (mSessions) {
        mSessions.put(sessionId, session);
    }
    //将InstallSource放入到PKMS的Setting调集中
    mPm.addInstallerPackageName(session.getInstallSource());
}

createSessionInternal进程首要便是创立了PackageInstallerSession方针,并将方针放入到mSessions调集中。

2.翻开session

翻开Session也是调用PackageInstallerService的createSession办法,内部调用openSessionInternal进行翻开。

private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
    synchronized (mSessions) {
        final PackageInstallerSession session = mSessions.get(sessionId);
        if (!checkOpenSessionAccess(session)) {
            throw new SecurityException("Caller has no access to session " + sessionId);
        }
        session.open();
        return session;
    }
}

内容很简单,经过sessionId去mSessions获取Session方针,然后调用session.open()翻开。

//frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
public void open() throws IOException {
    ...
    boolean wasPrepared;
    synchronized (mLock) {
        wasPrepared = mPrepared;
        if (!mPrepared) {
            if (stageDir != null) {
                prepareStageDir(stageDir);
            }
            mPrepared = true;
        }
    }
}
static void prepareStageDir(File stageDir) throws IOException {
​
    try {
        Os.mkdir(stageDir.getAbsolutePath(), 0775);
        Os.chmod(stageDir.getAbsolutePath(), 0775);
    } catch (ErrnoException e) {
    }
​
}

open办法也仅仅调用Os的mkdir进行stageDir文件夹创立,并且给stageDir文件夹设置了对应的权限、 stageDir临时文件夹途径:new File(“data/app”, “vmdl” + sessionId + “.tmp”);

3.copy apk文件到Session中

Session文件的写操作openWrite终究调用的是PackageInstallerSession的doWriteInternal,写文件就不介绍了,这个大家都非常清楚了。 只要知道文件是写入到的是stageDir临时文件夹(“data/app/vmdl{$sessionId}.tmp”)下面

4.调用commit进行装置。

public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
    ...
    dispatchSessionSealed();
}
private void dispatchSessionSealed() {
    mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
}
​
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
​
            case MSG_ON_SESSION_SEALED:
                //内部发射一个MSG_STREAM_VALIDATE_AND_COMMIT msg
                handleSessionSealed();
                break;
            case MSG_STREAM_VALIDATE_AND_COMMIT:
                //内部发射一个MSG_INSTALL msg
                handleStreamValidateAndCommit();
                break;
            case MSG_INSTALL:
                //处理运用装置进程
                handleInstall();
                break;
            case MSG_ON_PACKAGE_INSTALLED:
                final SomeArgs args = (SomeArgs) msg.obj;
                final String packageName = (String) args.arg1;
                final String message = (String) args.arg2;
                final Bundle extras = (Bundle) args.arg3;
                final IntentSender statusReceiver = (IntentSender) args.arg4;
                final int returnCode = args.argi1;
                args.recycle();
                sendOnPackageInstalled(mContext, statusReceiver, sessionId,
                        isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
                        packageName, returnCode, message, extras);
                break;
            case MSG_SESSION_VALIDATION_FAILURE:
                final int error = msg.arg1;
                final String detailMessage = (String) msg.obj;
                onSessionValidationFailure(error, detailMessage);
                break;
        }
        return true;
    }
​
};

发送MSG_ON_SESSION_SEALED的msg调用handleSessionSealed办法。handleSessionSealed办法内部又发送了MSG_STREAM_VALIDATE_AND_COMMIT的msg。 然后在handleStreamValidateAndCommit又发送了MSG_INSTALL。所以终究调用了handleInstall办法进行装置。

handleInstall办法能够大致分为:

  • 1.apk文件的校验
  • 2.apk文件的装置
@WorkerThread
private void handleInstall() {
   ...
   if (params.isStaged) {
       mStagedSession.verifySession();
   } else {
       verify();
   }
}

mStagedSession.verifySession终究也会走到verify,能够直接看verify办法

private void verify() {
    try {
        //1.创立装置apk需求的文件夹:
        prepareInheritedFiles();
        //2.解析APK文件并提取so库文件。其实便是解析AndroidManfest中的四大组件信息
        parseApkAndExtractNativeLibraries();
        //3.查验apk的进程
        verifyNonStaged();
    } catch (PackageManagerException e) {
        final String completeMsg = ExceptionUtils.getCompleteMessage(e);
        final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
        setSessionFailed(e.error, errorMsg);
        onSessionVerificationFailure(e.error, errorMsg);
    }
}

重点来看3.查验apk的进程,verifyNonStaged在经过一系列session检查之后,终究会调用到PackageSessionVerifier的verifyAPK办法, verifyAPK内部设置了装置成果监听IPackageInstallObserver2:

//frameworks/base/services/core/java/com/android/server/pm/PackageSessionVerifier.java
private void verifyAPK(PackageInstallerSession session, Callback callback)
       throws PackageManagerException {
    final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {
        @Override
        public void onUserActionRequired(Intent intent) {
            throw new IllegalStateException();
        }
        @Override
        public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                Bundle extras) {
            if (session.isStaged() && returnCode == PackageManager.INSTALL_SUCCEEDED) {
                // Continue verification for staged sessions
                verifyStaged(session.mStagedSession, callback);
                return;
            }
            if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
                String errorMessage = PackageManager.installStatusToString(returnCode, msg);
                session.setSessionFailed(returnCode, errorMessage);
                callback.onResult(returnCode, msg);
            } else {
                session.setSessionReady();
                callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
            }
        }
    };
    final VerificationParams verifyingSession = makeVerificationParams(session, observer);
    ...
    verifyingSession.verifyStage();
​
}
frameworks/base/services/core/java/com/android/server/pm/VerificationParams.java
public void verifyStage() {
    mPm.mHandler.post(this::startCopy);
}

能够看到verifyStage终究调用了mPm.mHandler post了一个startCopy的任务。

final void startCopy() {
    handleStartCopy();
    handleReturnCode();
}

handleStartCopy和handleReturnCode是两个笼统办法:详细完结是在VerificationParams类中

public void handleStartCopy() {
    //获取需求装置的apk包信息
    PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
            mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
    //校验需求更新的app的VersionCode,这儿面会对VersionCode版别和原始版别进行校验。
    Pair<Integer, String> ret = mInstallPackageHelper.verifyReplacingVersionCode(
            pkgLite, mRequiredInstalledVersionCode, mInstallFlags);
​
    if (!mOriginInfo.mExisting) {
        //假如是PackageManager.INSTALL_APEX不是APEX包,也便是apk包,则调用sendApkVerificationRequest对APK包进行更新
        if ((mInstallFlags & PackageManager.INSTALL_APEX) == 0) {   
            sendApkVerificationRequest(pkgLite);//关注点1
        }
        //回溯版别走的
        if ((mInstallFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
            sendEnableRollbackRequest();
        }
    }
​
}
//关注点1
private void sendApkVerificationRequest(PackageInfoLite pkgLite) {
    ...
    //发送完好的校验恳求
    sendIntegrityVerificationRequest(verificationId, pkgLite, verificationState);
    //发送package装置包校验
    sendPackageVerificationRequest(
            verificationId, pkgLite, verificationState);
    ...
}

sendPackageVerificationRequest首要校验下面几个:

  • 1.四大组件包信息校验
  • 2.apk打包公钥校验
  • 3.校验打包的sdk版别信息,经过添加广播的办法进行。

好了,持续回到startCopy的handleReturnCode办法

//frameworks/base/services/core/java/com/android/server/pm/VerificationParams.java
void handleReturnCode() {
    sendVerificationCompleteNotification();
}
private void sendVerificationCompleteNotification() {
    ...
    try {
        mObserver.onPackageInstalled(null, mRet, mErrorMessage,
                new Bundle());
    } catch (RemoteException e) {
        Slog.i(TAG, "Observer no longer exists.");
    }
    ...
}

在校验完毕成功今后,回调mObserver的onPackageInstalled办法。 而mObserver之前说过是在verifyAPK办法时传入的。

final IPackageInstallObserver2 observer = new IPackageInstallObserver2.Stub() {
​
    @Override
    public void onPackageInstalled(String basePackageName, int returnCode, String msg,
            Bundle extras) {
        ...
        if (returnCode != PackageManager.INSTALL_SUCCEEDED) {
            String errorMessage = PackageManager.installStatusToString(returnCode, msg);
            session.setSessionFailed(returnCode, errorMessage);
            callback.onResult(returnCode, msg);
        } else {
            session.setSessionReady();
            callback.onResult(PackageManager.INSTALL_SUCCEEDED, null);
        }
    }
​
};

onPackageInstalled回调成功后会再次调用callback的onResult办法

callBack是在前面剖析的PackageInstallerSession的verifyNonStaged办法中传入的,一层一层向外回调。

private void verifyNonStaged()
        throws PackageManagerException {
​
    mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
        mHandler.post(() -> {
            if (dispatchPendingAbandonCallback()) {
                // No need to continue if abandoned
                return;
            }
            if (error == INSTALL_SUCCEEDED) {
                onVerificationComplete();
            } else {
                onSessionVerificationFailure(error, msg);
            }
        });
    });
​
}

终究回调到onVerificationComplete办法,能够看到前面很大一部分是在对运用进行校验的部分。

下面剖析的才是详细装置的进程:

@WorkerThread
private void onVerificationComplete() {
	...
	install();
}
private CompletableFuture<Void> install() {
	List<CompletableFuture<InstallResult>> futures = installNonStaged();
	...
}
private List<CompletableFuture<InstallResult>> installNonStaged() {
	...	
	final InstallParams installingSession = makeInstallParams(future);	
	installingSession.installStage();
	...
}
frameworks/base/services/core/java/com/android/server/pm/InstallParams.java
public void installStage() {
	final Message msg = mPm.mHandler.obtainMessage(INIT_COPY);
	msg.obj = this;
	mPm.mHandler.sendMessage(msg);
}

installStage发送了一个INIT_COPY的msg,定位到mPm = PackageManagerImpl.java mPm.mHandler = PackageHandler

final class PackageHandler extends Handler {
	@Override
    public void handleMessage(Message msg) {
        try {
            doHandleMessage(msg);
        } finally {
            Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
        }
    }
    void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
                HandlerParams params = (HandlerParams) msg.obj;
                if (params != null) {
                    ...
                    params.startCopy();                
                }
                break;
            }
    	}
    }
}

INIT_COPY的msg调用了HandlerParams的startCopy办法处理,而这个时分的HandlerParams的完结类是InstallParams.java,前面校验进程中的完结类是VerificationParams

final void startCopy() {
	handleStartCopy();//关注点1
	handleReturnCode();//关注点2
}

先看关注点1

//frameworks/base/services/core/java/com/android/server/pm/InstallParams.java
public void handleStartCopy() {
	PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mPm.mContext,
			mPackageLite, mOriginInfo.mResolvedPath, mInstallFlags, mPackageAbiOverride);
	if (!mOriginInfo.mStaged && pkgLite.recommendedInstallLocation
			== InstallLocationUtils.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
		//先开释一部分不需求的缓存。
		pkgLite.recommendedInstallLocation = mPm.freeCacheForInstallation(
				pkgLite.recommendedInstallLocation, mPackageLite,
				mOriginInfo.mResolvedPath, mPackageAbiOverride, mInstallFlags);
	}
	//依据默许的规矩重写装置途径,首要是区分运用外置sdcard途径仍是内置途径
	mRet = overrideInstallLocation(pkgLite.packageName, pkgLite.recommendedInstallLocation,
			pkgLite.installLocation);
}
再看关注点2:
void handleReturnCode() {
	processPendingInstall();
}
private void processPendingInstall() {
	//创立装置的参数信息
	InstallArgs args = createInstallArgs(this);
	if (mRet == PackageManager.INSTALL_SUCCEEDED) {
		//关注点3 复制apk
		mRet = args.copyApk();
	}
	if (mRet == PackageManager.INSTALL_SUCCEEDED) {
		F2fsUtils.releaseCompressedBlocks(
				mPm.mContext.getContentResolver(), new File(args.getCodePath()));
	}
	if (mParentInstallParams != null) {
		mParentInstallParams.tryProcessInstallRequest(args, mRet);
	} else {
		PackageInstalledInfo res = new PackageInstalledInfo(mRet);
		//关注点4
		processInstallRequestsAsync(
				res.mReturnCode == PackageManager.INSTALL_SUCCEEDED,
				Collections.singletonList(new InstallRequest(args, res)));
	}
}

processPendingInstall关注两个部分:

  • 1.复制apk
  • 2.装置apk

1.复制apk:mRet = args.copyApk();

而args 是FileInstallArgs类方针

//frameworks/base/services/core/java/com/android/server/pm/FileInstallArgs.java
int copyApk() {
    return doCopyApk();
}
private int doCopyApk() {
    //1.给StageDir分配对应的临时文件夹以及权限
    final File tempDir = mPm.mInstallerService.allocateStageDirLegacy(mVolumeUuid, isEphemeral);    
    mCodeFile = tempDir;
    //2.复制Package,这儿面首要是四大组件信息的复制
    int ret = PackageManagerServiceUtils.copyPackage(
            mOriginInfo.mFile.getAbsolutePath(), mCodeFile);
​
    //3.依据abifilter 复制NativeLibrary ,so库到对应的lib目录下
    handle = NativeLibraryHelper.Handle.create(mCodeFile);
    ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
            mAbiOverride, isIncremental);
    return ret;
​
}

先来剖析1处allocateStageDirLegacy

public File allocateStageDirLegacy(String volumeUuid, boolean isEphemeral) throws IOException {
    synchronized (mSessions) {
        try {
            final int sessionId = allocateSessionIdLocked();
            mLegacySessions.put(sessionId, true);
            final File sessionStageDir = buildTmpSessionDir(sessionId, volumeUuid);
            prepareStageDir(sessionStageDir);
            return sessionStageDir;
        } catch (IllegalStateException e) {
            throw new IOException(e);
        }
    }
}

看buildTmpSessionDir,这个前面也剖析过,终究回来的File途径为:data/app/vmdl{$sessionId}.tmp

再来剖析2处PackageManagerServiceUtils.copyPackage

public static int copyPackage(String packagePath, File targetDir) {
    try {
        final File packageFile = new File(packagePath);
        //解析APK文件到ParseResult中
        final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
                input.reset(), packageFile, /* flags */ 0);
        //获取apk的文件PackageLite信息
        final PackageLite pkg = result.getResult();
        //复制file,核心办法
        copyFile(pkg.getBaseApkPath(), targetDir, "base.apk");
​
        return PackageManager.INSTALL_SUCCEEDED;
    } catch (IOException | ErrnoException e) {
    }
​
}
private static void copyFile(String sourcePath, File targetDir, String targetName)
        throws ErrnoException, IOException {
​
    final File targetFile = new File(targetDir, targetName);
    final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
            O_RDWR | O_CREAT, 0644);
    Os.chmod(targetFile.getAbsolutePath(), 0644);
    FileInputStream source = null;
    try {
        source = new FileInputStream(sourcePath);
        FileUtils.copy(source.getFD(), targetFd);
    } finally {
        IoUtils.closeQuietly(source);
    }
​
}

copyPackage会先去解析apk文件,然后调用copyFile办法,copyFile中调用Os.open去翻开targetFile方针文件, 调用FileUtils.copy办法将原文件复制到方针文件中

2.装置apk:processInstallRequestsAsync

// Queue up an async operation since the package installation may take a little while.
private void processInstallRequestsAsync(boolean success,
		List<InstallRequest> installRequests) {
	mPm.mHandler.post(() -> {
		mInstallPackageHelper.processInstallRequests(success, installRequests);
	});
}
frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
public void processInstallRequests(boolean success, List<InstallRequest> installRequests) {
	List<InstallRequest> apkInstallRequests = new ArrayList<>();
	for (InstallRequest request : installRequests) {
		...
		apkInstallRequests.add(request);
	}
	if (success) {
		for (InstallRequest request : apkInstallRequests) {
			//预装置,内部首要是做clean操作
			request.mArgs.doPreInstall(request.mInstallResult.mReturnCode);
		}
		synchronized (mPm.mInstallLock) {
			//实践装置apk进程
			installPackagesTracedLI(apkInstallRequests);
		}
	}
	...
}
private void installPackagesTracedLI(List<InstallRequest> requests) {
	...
	installPackagesLI(requests);
	...
}

下面咱们重点来剖析下installPackagesLI

private void installPackagesLI(List<InstallRequest> requests) {
​
    for (InstallRequest request : requests) {       
        //阶段1:prepare阶段
        repareResult = preparePackageLI(request.mArgs, request.mInstallResult);         
        //阶段2:scan阶段
        final ScanResult result = scanPackageTracedLI(
                prepareResult.mPackageToScan, prepareResult.mParseFlags,
                prepareResult.mScanFlags, System.currentTimeMillis(),
                request.mArgs.mUser, request.mArgs.mAbiOverride);       
    }   
    //阶段3:Reconcile阶段
    reconciledPackages = ReconcilePackageUtils.reconcilePackages(
            reconcileRequest, mSharedLibraries,
            mPm.mSettings.getKeySetManagerService(), mPm.mSettings);
    }
    commitRequest = new CommitRequest(reconciledPackages,
                 mPm.mUserManager.getUserIds());
    //阶段4:Commit阶段
    commitPackagesLocked(commitRequest);
    //阶段5:完结apk装置
    executePostCommitSteps(commitRequest);
​
}

installPackagesLI是终究装置运用的办法:首要分为4个阶段

  • 阶段1:prepare阶段:剖析当前装置包的状况,解析装置包并对其做初始化验证
  • 阶段2:scan阶段:依据prepare阶段中搜集的装置包状况信息去扫描解分出来的包
  • 阶段3:Reconcile阶段:验证scan阶段扫描到的Package信息以及当前体系状况,确保apk的正确装置。
  • 阶段4:Commit阶段:提交一切扫描的包并更新体系状况。这是仅有能够在装置流程和一切可预测错误中修正体系状况的当地.

在 preparePackageLI() 内运用 PackageParser2.parsePackage() 解析AndroidManifest.xml,获取四大组件等信息; 运用ParsingPackageUtils.getSigningDetails() 解析签名信息;重命名包终究途径 等。

完结了解析和校验准备工作后,终究一步便是对apk的装置了。这儿调用了executePostCommitSteps准备app数据,并履行dex优化

终究经过executePostCommitSteps完结apk的装置,履行dex优化等操作

  • 阶段5:完结apk装置

    - private void executePostCommitSteps(CommitRequest commitRequest) {
    ​
        for (ReconciledPackage reconciledPkg : commitRequest.mReconciledPackages.values()) {
            // prepareAppDataPostCommitLIF经过一系列调用会走到Installer的createAppData办法。
            mAppDataHelper.prepareAppDataPostCommitLIF(pkg, 0);
            /*
            检测是否需求进行dex优化:一起满意下面三种状况就需求
            1.不是一个即时运用app或许假如是的话经过gservices进行dex优化操作
            2.debuggable为false
            3.不在增量文件体系上。
            */
            final boolean performDexopt =
                    (!instantApp || android.provider.Settings.Global.getInt(
                            mContext.getContentResolver(),
                            android.provider.Settings.Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
                            && !pkg.isDebuggable()
                            && (!onIncremental)
                            && dexoptOptions.isCompilationEnabled();
            if (performDexopt) {
                //获取so库所在的目录
                PackageSetting realPkgSetting = result.mExistingSettingCopied
                        ? result.mRequest.mPkgSetting : result.mPkgSetting;
                boolean isUpdatedSystemApp = reconciledPkg.mPkgSetting.getPkgState()
                        .isUpdatedSystemApp();
                //更新体系app信息。
                realPkgSetting.getPkgState().setUpdatedSystemApp(isUpdatedSystemApp);
                //进行dex优化
                mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
                        null /* instructionSets */,
                        mPm.getOrCreateCompilerPackageStats(pkg),
                        mDexManager.getPackageUseInfoOrDefault(packageName),
                        dexoptOptions);
            }
            //告诉BackgroundDexOptService服务当前packageName的运用进行了更新。
            BackgroundDexOptService.getService().notifyPackageChanged(packageName);
            notifyPackageChangeObserversOnUpdate(reconciledPkg);
        }
    ​
    }
    

阶段5:首要做了下面两件事

  • 任务1:调用prepareAppDataPostCommitLIF办法,终究履行到createAppData办法进行app的装置.

    public @NonNull CreateAppDataResult createAppData(@NonNull CreateAppDataArgs args)
        throws InstallerException {
    ...
    try {
        return mInstalld.createAppData(args);
    } catch (Exception e) {
        throw InstallerException.from(e);
    }
    }
    

mInstalld在前面剖析过了,installd进程 的履行权限为 root,一切实践的运用装置,卸载等操作都是经过这个服务进行的。 PKMS仅仅java层的封装。mInstalld进程和PKMS是经过binder进行通讯的。

  • 任务2.调用performDexOpt进行dex优化 一起满意下面三种状况就需求

    • 1.不是一个即时运用app或许假如是的话经过gservices进行dex优化操作
    • 2.debuggable为false
    • 3.不在增量文件体系上。

然后关于dex优化部分,后面会单独出一篇文章来解说。

关于运用装置部分就讲到这儿了。

总结

关于Android中保证理机制,由于源码部分内容较多,小余运用了两篇文章来解说。期望你能从中有所收获。

上一篇:根据Android T:保证理机制详解(上)

假如文字对你协助,帮助给小余点个赞,谢啦。

参阅:

一篇文章看明白 Android PackageManagerService 工作流程

Android T谷歌官方文档

深入了解安卓-了解一下 Android 10 中的 APEX

android overlay机制实践

Android 体系服务 PMS Installd 守护进程(二)

Android FileProvider介绍

ANDROID 保证理(PACKAGEMANAGERSERVICE)

android体系源码目录system/framework下各个jar包的用处

同类文章:

“一文读懂”系列:Android屏幕刷新机制

Android Framework常识收拾:WindowManager体系(上)

“一文读懂”系列:Android中的硬件加速

“framework必会”系列:Android Input体系(一)事件读取机制

“framework必会”系列:Android Input体系(二)事件分发机制

“一文读懂”系列:无处不在的WMS

“一文读懂”系列:AMS是怎么动态办理进程的?

不知道怎么看源码?试试这几种办法~