今天简单讲解一下PackageInstaller

文件路径:

packages/apps/PackageInstaller

frameworks/base/core/java/android/content/pm&res

下面开始讲解:

首先,我们说一下安装apk的几种方式,整体上可以分为2类,一类是有界面安装,一类是无界面安装。无界面安装分为内置apk开机安装和命令安装,命令安装又分为两类,一类电脑安装也就是adb命令,另一类是手机安装也就是pm命令。今天我们主要介绍有界面安装。

当然,我们从这个安装界面说起,这个界面是那个呢?就是PackageInstallerActivity这个acitvity。它是如何启动起来的呢?我们去看看它在AndroidManifest是如何定义的
<activity android:name=".PackageInstallerActivity"          android:configChanges="orientation|keyboardHidden|screenSize"          android:excludeFromRecents="true"          android:screenOrientation="unspecified">      <intent-filter>          <action android:name="android.intent.action.VIEW" />          <action android:name="android.intent.action.INSTALL_PACKAGE" />          <category android:name="android.intent.category.DEFAULT" />          <data android:scheme="content" />          <data android:scheme="file" />          <data android:mimeType="application/vnd.android.package-archive" />      </intent-filter>      <intent-filter>          <action android:name="android.intent.action.INSTALL_PACKAGE" />          <category android:name="android.intent.category.DEFAULT" />          <data android:scheme="content" />          <data android:scheme="file" />      </intent-filter>  </activity>

很明显了,我们可以通过android.intent.action.INSTALL_PACKAGE这个action启动,也可以通过android.intent.action.VIEW这个action加上"application/vnd.android.package-archive"这个type启动,当然不加这个type也能启动,但是会找到很多这样的activity哦。另外,通过类名或包名启动也未尝不可的。所以,大部分启动是这样的
String apkFileString = Environment.getExternalStorageDirectory().getAbsolutePath()+"/.../packageName.pac";  File apkFile = new File(apkFileString);  Intent intent = new Intent(Intent.ACTION_VIEW);  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");  mContext.startActivity(intent);

这里我们传进去一个数据就是pakFile的Uri,然后我们去PackageInstallerActivity的onCreate中看看
final Intent intent = getIntent();  mPackageURI = intent.getData();  mPm = getPackageManager();  mPkgInfo = PackageUtil.getPackageInfo(mPackageURI);

获取到,我们刚才传进来的apkFile的Uri给了mPackageURI,接着获取到PackageManager,然后生成一个mPkgInfo也就是PackageParser.Package,这个很重要。我们看看PackageParser.Package是如何生成的,PackageParser.Package里面都包含了什么东西。那我们就要去PackageUtil.getPackageInfo中了
public static  PackageParser.Package getPackageInfo(Uri packageURI) {      final String archiveFilePath = packageURI.getPath();      PackageParser packageParser = new PackageParser(archiveFilePath);      File sourceFile = new File(archiveFilePath);      DisplayMetrics metrics = new DisplayMetrics();      metrics.setToDefaults();      PackageParser.Package pkg =  packageParser.parsePackage(sourceFile,              archiveFilePath, metrics, 0);      // Nuke the parser reference.      packageParser = null;      return pkg;  }

生成一个Package解析器,通过这个解析器来获取到PackageParser.Package中需要的数据,生成一个PackageParser.Package。我们看看PackageParser.parsePackage是如何生成一个PackageParser.Package的,这里传进去四个参数,一个Source File,apk文件,一个apk路径,一个屏幕信息,最后一个0,具体做什么的,进去之后就能明白了
public Package parsePackage(File sourceFile, String destCodePath,          DisplayMetrics metrics, int flags) {      mParseError = PackageManager.INSTALL_SUCCEEDED;        mArchiveSourcePath = sourceFile.getPath();      if (!sourceFile.isFile()) {          Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);          mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;          return null;      }      if (!isPackageFilename(sourceFile.getName())              && (flags&PARSE_MUST_BE_APK) != 0) {          if ((flags&PARSE_IS_SYSTEM) == 0) {              // We expect to have non-.apk files in the system dir,              // so don't warn about them.              Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);          }          mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;          return null;      }        if (DEBUG_JAR)          Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);        XmlResourceParser parser = null;      AssetManager assmgr = null;      Resources res = null;      boolean assetError = true;      try {          assmgr = new AssetManager();          int cookie = assmgr.addAssetPath(mArchiveSourcePath);          if (cookie != 0) {              res = new Resources(assmgr, metrics, null);              assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                      Build.VERSION.RESOURCES_SDK_INT);              parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);              assetError = false;          } else {              Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);          }      } catch (Exception e) {          Slog.w(TAG, "Unable to read AndroidManifest.xml of "                  + mArchiveSourcePath, e);      }      if (assetError) {          if (assmgr != null) assmgr.close();          mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;          return null;      }      String[] errorText = new String[1];      Package pkg = null;      Exception errorException = null;      try {          // XXXX todo: need to figure out correct configuration.          pkg = parsePackage(res, parser, flags, errorText);      } catch (Exception e) {          errorException = e;          mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;      }          if (pkg == null) {          // If we are only parsing core apps, then a null with INSTALL_SUCCEEDED          // just means to skip this app so don't make a fuss about it.          if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {              if (errorException != null) {                  Slog.w(TAG, mArchiveSourcePath, errorException);              } else {                  Slog.w(TAG, mArchiveSourcePath + " (at "                          + parser.getPositionDescription()                          + "): " + errorText[0]);              }              if (mParseError == PackageManager.INSTALL_SUCCEEDED) {                  mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;              }          }          parser.close();          assmgr.close();          return null;      }        parser.close();      assmgr.close();        // Set code and resource paths      pkg.mPath = destCodePath;      pkg.mScanPath = mArchiveSourcePath;      //pkg.applicationInfo.sourceDir = destCodePath;      //pkg.applicationInfo.publicSourceDir = destRes;      pkg.mSignatures = null;        return pkg;  }

首先sourceFile.isFile()判断一下是不是文件,如果不是,返回;接着isPackageFilename(sourceFile.getName())判断是不是apk文件,如果不是,返回;接着去获取三个关键变量,也就是
XmlResourceParser parser = null;  AssetManager assmgr = null;  Resources res = null;

这三个是什么呢?这里简单说一下,AssetManager资产管理器,用来管理包中获取到的资源
assmgr = new AssetManager();  int cookie = assmgr.addAssetPath(mArchiveSourcePath);

通过addAssetPath可以获取到唯一标识该apk包资产的关键字cookie,也就是通过cookie可以找到该包的资源信息。Resources就是资源了,包括图片,color,xml等资源
res = new Resources(assmgr, metrics, null);

当然Resources信息也是通过AssetManager获取到的。XmlResourceParser顾名思义就是Xml资源文件解析器了,用来解析我们xml文件的
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

ANDROID_MANIFEST_FILENAME也就是
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";

这样就很明显了,这里生成的xml文件资源解析器是用来解析AndroidManifest文件的了。接下来就是关键了
String[] errorText = new String[1];  Package pkg = null;  Exception errorException = null;  try {      // XXXX todo: need to figure out correct configuration.      pkg = parsePackage(res, parser, flags, errorText);  } catch (Exception e) {      errorException = e;      mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;  }

这里才是我们Package真正生成的地方了,也就是pkg = parsePackage(res, parser, flags, errorText)了。parsePackage是同构函数,一个是以File为首个参数,就是我们现在分析的这个,一个是以Resources为首个参数,就是我们接下来要讲的了,由于这个函数比较大,所以不再全部列出,只选取主要的
String pkgName = parsePackageName(parser, attrs, flags, outError);

获取包名。
final Package pkg = new Package(pkgName);  boolean foundApp = false;    TypedArray sa = res.obtainAttributes(attrs,          com.android.internal.R.styleable.AndroidManifest);  pkg.mVersionCode = sa.getInteger(          com.android.internal.R.styleable.AndroidManifest_versionCode, 0);  pkg.mVersionName = sa.getNonConfigurationString(          com.android.internal.R.styleable.AndroidManifest_versionName, 0);  if (pkg.mVersionName != null) {      pkg.mVersionName = pkg.mVersionName.intern();  }  String str = sa.getNonConfigurationString(          com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);  if (str != null && str.length() > 0) {      String nameError = validateName(str, true);      if (nameError != null && !"android".equals(pkgName)) {          outError[0] = "<manifest> specifies bad sharedUserId name \""              + str + "\": " + nameError;          mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;          return null;      }      pkg.mSharedUserId = str.intern();      pkg.mSharedUserLabel = sa.getResourceId(              com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);  }  sa.recycle();    pkg.installLocation = sa.getInteger(          com.android.internal.R.styleable.AndroidManifest_installLocation,          PARSE_DEFAULT_INSTALL_LOCATION);  pkg.applicationInfo.installLocation = pkg.installLocation;

解析获取,我们AndroidManifest的attrs,也就是frameworks/base/core/res/res/values下的attrs_manifest.xml中定义的
<declare-styleable name="AndroidManifest">      <attr name="versionCode" />      <attr name="versionName" />      <attr name="sharedUserId" />      <attr name="sharedUserLabel" />      <attr name="installLocation" />  </declare-styleable>

这些变量信息。接下来就是一个大循环了,这里解析的内容比较多了,我们举几个常见的例子,如"application"也就是
<application android:label="@string/app_name">  </application>

这里面包含的信息,例如这里的lable等等,还有以其为父的"activity","receiver","service","provider"等等,这里以"activity"为例,还是去frameworks/base/core/res/res/values下的attrs_manifest.xml中,也就是
<declare-styleable name="AndroidManifestActivity" parent="AndroidManifestApplication">      <!-- Required name of the class implementing the activity, deriving from          {@link android.app.Activity}.  This is a fully          qualified class name (for example, com.mycompany.myapp.MyActivity); as a          short-hand if the first character of the class          is a period then it is appended to your package name. -->      <attr name="name" />      <attr name="theme" />      <attr name="label" />      <attr name="description" />      <attr name="icon" />      <attr name="logo" />      <attr name="launchMode" />      <attr name="screenOrientation" />      <attr name="configChanges" />      <attr name="permission" />      <attr name="multiprocess" />      <attr name="process" />      <attr name="taskAffinity" />      <attr name="allowTaskReparenting" />      <attr name="finishOnTaskLaunch" />      <attr name="finishOnCloseSystemDialogs" />      <attr name="clearTaskOnLaunch" />      <attr name="noHistory" />      <attr name="alwaysRetainTaskState" />      <attr name="stateNotNeeded" />      <attr name="excludeFromRecents" />      <!-- Specify whether the activity is enabled or not (that is, can be instantiated by the system).           It can also be specified for an application as a whole, in which case a value of "false"           will override any component specific values (a value of "true" will not override the           component specific values). -->      <attr name="enabled" />      <attr name="exported" />      <!-- Specify the default soft-input mode for the main window of           this activity.  A value besides "unspecified" here overrides           any value in the theme. -->      <attr name="windowSoftInputMode" />      <attr name="immersive" />      <attr name="hardwareAccelerated" />      <attr name="uiOptions" />  </declare-styleable>

这里有很多变量,在定义一个acitivity的时候有的我们用过,有的没有用过,Xml文件资源解析器就是从xml中获取到这先变量的值然后付给这些变量;同样还有"permission"权限,也就是
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />

这类,大家经常会见到的;还有"permission-group","uses-sdk"等等吧,有兴趣的可以一一研究。最终这些信息都会囊括到我们的Package中。这里我们明白Package是什么了吧?就是包含包中所有信息的的玩意。到此位置我们的Package已经生成,然后我们还回到PackageInstallerActivity的onCreate中,接着往下看,不重要的就跳过了
initiateInstall()

也就是
private void initiateInstall() {      String pkgName = mPkgInfo.packageName;      // Check if there is already a package on the device with this name      // but it has been renamed to something else.      String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });      if (oldName != null && oldName.length > 0 && oldName[0] != null) {          pkgName = oldName[0];          mPkgInfo.setPackageName(pkgName);      }      // Check if package is already installed. display confirmation dialog if replacing pkg      try {          mAppInfo = mPm.getApplicationInfo(pkgName,                  PackageManager.GET_UNINSTALLED_PACKAGES);      } catch (NameNotFoundException e) {          mAppInfo = null;      }      if (mAppInfo == null || getIntent().getBooleanExtra(Intent.EXTRA_ALLOW_REPLACE, false)) {          startInstallConfirm();      } else {          if(localLOGV) Log.i(TAG, "Replacing existing package:"+                  mPkgInfo.applicationInfo.packageName);          showDialogInner(DLG_REPLACE_APP);      }  }

然后是这里的startInstallConfirm(),也就是
private void startInstallConfirm() {      LinearLayout permsSection = (LinearLayout) mInstallConfirm.findViewById(R.id.permissions_section);      LinearLayout securityList = (LinearLayout) permsSection.findViewById(              R.id.security_settings_list);      boolean permVisible = false;      if(mPkgInfo != null) {          AppSecurityPermissions asp = new AppSecurityPermissions(this, mPkgInfo);          if(asp.getPermissionCount() > 0) {              permVisible = true;              securityList.addView(asp.getPermissionsView());          }      }      if(!permVisible){          permsSection.setVisibility(View.INVISIBLE);      }      mInstallConfirm.setVisibility(View.VISIBLE);      mOk = (Button)findViewById(R.id.ok_button);      mCancel = (Button)findViewById(R.id.cancel_button);      mOk.setOnClickListener(this);      mCancel.setOnClickListener(this);  }

到这里我们的PackageInstallerActivity这个activity才算完成,这里我们看看我们安装界面的权限View是如何生成的,也就是asp.getPermissionsView(),这里的AppSecurityPermissions(this, mPkgInfo)传进去两个参数,一个是Context,一个是我们刚才获取到的Package,我们进去看看,文件在frameworks/base/core/java/android/widget下面
public AppSecurityPermissions(Context context, PackageParser.Package pkg) {      mContext = context;      mPm = mContext.getPackageManager();      mPermsList = new ArrayList<PermissionInfo>();      Set<PermissionInfo> permSet = new HashSet<PermissionInfo>();      if(pkg == null) {          return;      }      // Get requested permissions      if (pkg.requestedPermissions != null) {          ArrayList<String> strList = pkg.requestedPermissions;          int size = strList.size();          if (size > 0) {              extractPerms(strList.toArray(new String[size]), permSet);          }      }      // Get permissions related to  shared user if any      if(pkg.mSharedUserId != null) {          int sharedUid;          try {              sharedUid = mPm.getUidForSharedUser(pkg.mSharedUserId);              getAllUsedPermissions(sharedUid, permSet);          } catch (NameNotFoundException e) {              Log.w(TAG, "Could'nt retrieve shared user id for:"+pkg.packageName);          }      }      // Retrieve list of permissions      for(PermissionInfo tmpInfo : permSet) {          mPermsList.add(tmpInfo);      }  }

就是获取到一个PermissionInfo的List就是mPermsList。然后我们看看asp.getPermissionsView(),也就是
public View getPermissionsView() {            mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);      mPermsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);      mShowMore = mPermsView.findViewById(R.id.show_more);      mShowMoreIcon = (ImageView) mShowMore.findViewById(R.id.show_more_icon);      mShowMoreText = (TextView) mShowMore.findViewById(R.id.show_more_text);      mDangerousList = (LinearLayout) mPermsView.findViewById(R.id.dangerous_perms_list);      mNonDangerousList = (LinearLayout) mPermsView.findViewById(R.id.non_dangerous_perms_list);      mNoPermsView = mPermsView.findViewById(R.id.no_permissions);        // Set up the LinearLayout that acts like a list item.      mShowMore.setClickable(true);      mShowMore.setOnClickListener(this);      mShowMore.setFocusable(true);        // Pick up from framework resources instead.      mDefaultGrpLabel = mContext.getString(R.string.default_permission_group);      mPermFormat = mContext.getString(R.string.permissions_format);      mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot);      mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission);      mShowMaxIcon = mContext.getResources().getDrawable(R.drawable.expander_close_holo_dark);      mShowMinIcon = mContext.getResources().getDrawable(R.drawable.expander_open_holo_dark);            // Set permissions view      setPermissions(mPermsList);      return mPermsView;  }

这里就是我们的权限View的布局了,也就是frameworks/base/core/res/res/layout下面的app_perms_summary.xml布局了,如果我们想修改权限VIew的话就要从这里开始了。我们去看看是如何生成的,也就是setPermissions(mPermsList)
private void setPermissions(List<PermissionInfo> permList) {      mGroupLabelCache = new HashMap<String, CharSequence>();      //add the default label so that uncategorized permissions can go here      mGroupLabelCache.put(mDefaultGrpName, mDefaultGrpLabel);            // Map containing group names and a list of permissions under that group      // categorized as dangerous      mDangerousMap = new HashMap<String, String>();      // Map containing group names and a list of permissions under that group      // categorized as normal      mNormalMap = new HashMap<String, String>();            // Additional structures needed to ensure that permissions are unique under       // each group      Map<String, List<PermissionInfo>> dangerousMap =           new HashMap<String,  List<PermissionInfo>>();      Map<String, List<PermissionInfo> > normalMap =           new HashMap<String,  List<PermissionInfo>>();      PermissionInfoComparator permComparator = new PermissionInfoComparator(mPm);            if (permList != null) {          // First pass to group permissions          for (PermissionInfo pInfo : permList) {              if(localLOGV) Log.i(TAG, "Processing permission:"+pInfo.name);              if(!isDisplayablePermission(pInfo)) {                  if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" is not displayable");                  continue;              }              Map<String, List<PermissionInfo> > permInfoMap =                  (pInfo.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS) ?                          dangerousMap : normalMap;              String grpName = (pInfo.group == null) ? mDefaultGrpName : pInfo.group;              if(localLOGV) Log.i(TAG, "Permission:"+pInfo.name+" belongs to group:"+grpName);              List<PermissionInfo> grpPermsList = permInfoMap.get(grpName);              if(grpPermsList == null) {                  grpPermsList = new ArrayList<PermissionInfo>();                  permInfoMap.put(grpName, grpPermsList);                  grpPermsList.add(pInfo);              } else {                  int idx = Collections.binarySearch(grpPermsList, pInfo, permComparator);                  if(localLOGV) Log.i(TAG, "idx="+idx+", list.size="+grpPermsList.size());                  if (idx < 0) {                      idx = -idx-1;                      grpPermsList.add(idx, pInfo);                  }              }          }          // Second pass to actually form the descriptions          // Look at dangerous permissions first          aggregateGroupDescs(dangerousMap, mDangerousMap);          aggregateGroupDescs(normalMap, mNormalMap);      }        mCurrentState = State.NO_PERMS;      if(mDangerousMap.size() > 0) {          mCurrentState = (mNormalMap.size() > 0) ? State.BOTH : State.DANGEROUS_ONLY;      } else if(mNormalMap.size() > 0) {          mCurrentState = State.NORMAL_ONLY;      }      if(localLOGV) Log.i(TAG, "mCurrentState=" + mCurrentState);      showPermissions();  }

这里区分一下是dangerousMap,也就是PermissionInfo.PROTECTION_DANGEROUS类权限还是normalMap一般权限,然后就去showPermissions()
private void showPermissions() {        switch(mCurrentState) {      case NO_PERMS:          displayNoPermissions();          break;        case DANGEROUS_ONLY:          displayPermissions(true);          break;        case NORMAL_ONLY:          displayPermissions(false);          break;        case BOTH:          displayPermissions(true);          if (mExpanded) {              displayPermissions(false);              mShowMoreIcon.setImageDrawable(mShowMaxIcon);              mShowMoreText.setText(R.string.perms_hide);              mNonDangerousList.setVisibility(View.VISIBLE);          } else {              mShowMoreIcon.setImageDrawable(mShowMinIcon);              mShowMoreText.setText(R.string.perms_show_all);              mNonDangerousList.setVisibility(View.GONE);          }          mShowMore.setVisibility(View.VISIBLE);          break;      }  }

给我们的布局赋显示的内容了,这里不一一解释,我们去看看displayPermissions
private void displayPermissions(boolean dangerous) {      Map<String, String> permInfoMap = dangerous ? mDangerousMap : mNormalMap;      LinearLayout permListView = dangerous ? mDangerousList : mNonDangerousList;      permListView.removeAllViews();        Set<String> permInfoStrSet = permInfoMap.keySet();      for (String loopPermGrpInfoStr : permInfoStrSet) {          CharSequence grpLabel = getGroupLabel(loopPermGrpInfoStr);          //guaranteed that grpLabel wont be null since permissions without groups          //will belong to the default group          if(localLOGV) Log.i(TAG, "Adding view group:" + grpLabel + ", desc:"                  + permInfoMap.get(loopPermGrpInfoStr));          permListView.addView(getPermissionItemView(grpLabel,                  permInfoMap.get(loopPermGrpInfoStr), dangerous));      }  }

看到这里就很明白了,我们的权限View是怎么生成的了。不再多做解释了。至此我们PackageInstallerActivity这个activity已经完全形成了,截个图吧
android PackageInstaller那点事儿
接下来,我们说说当点击“安装”之后做了什么事情。
public void onClick(View v) {      if(v == mOk) {          // 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, InstallAppProgress.class);          String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);          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(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);          startActivity(newIntent);          finish();      } else if(v == mCancel) {          // Cancel and finish          setResult(RESULT_CANCELED);          finish();      }  }

去启动了另外一个acitvity也就是InstallAppProgress,并过去几个数据,主要是mPkgInfo.applicationInfo也就是ApplicationInfo,还有mPackageURI也就是apkFile的Uri,还有一些其他的数据。然后我们就去InstallAppProgress的onCreate中
@Override  public void onCreate(Bundle icicle) {      super.onCreate(icicle);      Intent intent = getIntent();      mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);      mPackageURI = intent.getData();      initView();  }

获取到传过来的两个数据,然后就initView(),initView()里面是一些布局的初始化,不再赘述,只截取重要的,也就是
String installerPackageName = getIntent().getStringExtra(          Intent.EXTRA_INSTALLER_PACKAGE_NAME);  PackageInstallObserver observer = new PackageInstallObserver();  pm.installPackage(mPackageURI, observer, installFlags, installerPackageName);

pm是PackageManager,这样我们就去PackageManager.installPackage中,在PackageManager的installPackage是abstract函数,具体实现在PackageManagerService中,这里传进去的参数第一个我们已经知道了,第二个是package安装的观察者
class PackageInstallObserver extends IPackageInstallObserver.Stub {      public void packageInstalled(String packageName, int returnCode) {          Message msg = mHandler.obtainMessage(INSTALL_COMPLETE);          Log.d("packageInstalled", "returnCode = "+returnCode);          msg.arg1 = returnCode;          mHandler.sendMessage(msg);      }  }

当安装完成就会走到packageInstalled个函数中,第三个参数是flag主要标识是第一次安装,还是已经安装更新,第三个参数很明显是安装的包名了。然后我们去看看
/* Called when a downloaded package installation has been confirmed by the user */  public void installPackage(          final Uri packageURI, final IPackageInstallObserver observer, final int flags) {      installPackage(packageURI, observer, flags, null);  }    /* Called when a downloaded package installation has been confirmed by the user */  public void installPackage(          final Uri packageURI, final IPackageInstallObserver observer, final int flags,          final String installerPackageName) {      installPackageWithVerification(packageURI, observer, flags, installerPackageName, null,              null);  }

也就是installPackageWithVerification
public void installPackageWithVerification(Uri packageURI, IPackageInstallObserver observer,          int flags, String installerPackageName, Uri verificationURI,          ManifestDigest manifestDigest) {      mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);        final int uid = Binder.getCallingUid();        final int filteredFlags;        if (uid == Process.SHELL_UID || uid == 0) {          if (DEBUG_INSTALL) {              Slog.v(TAG, "Install from ADB");          }          filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;      } else {          filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;      }        final Message msg = mHandler.obtainMessage(INIT_COPY);      msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,              verificationURI, manifestDigest);      mHandler.sendMessage(msg);  }

就去发了一个消息INIT_COPY,并携带了我们传进来的参数组成的一个类InstallParams,InstallParams继承于HandlerParams,我们去看看这个消息执行了什么
case INIT_COPY: {      if (DEBUG_INSTALL) Slog.i(TAG, "init_copy");      HandlerParams params = (HandlerParams) msg.obj;      int idx = mPendingInstalls.size();      if (DEBUG_INSTALL) Slog.i(TAG, "idx=" + idx);      // If a bind was already initiated we dont really      // need to do anything. The pending install      // will be processed later on.      if (!mBound) {          // 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();              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;  }

这里先 mPendingInstalls.add(idx, params)把我们要安装的信息放到HandlerParams的一个List中mPendingInstalls,然后去发了一个消息MCS_BOUND,也就是
case MCS_BOUND: {      if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");      if (msg.obj != null) {          mContainerService = (IMediaContainerService) msg.obj;      }      if (mContainerService == null) {          // Something seriously wrong. Bail out          Slog.e(TAG, "Cannot bind to media container service");          for (HandlerParams params : mPendingInstalls) {              mPendingInstalls.remove(0);              // Indicate service bind error              params.serviceError();          }          mPendingInstalls.clear();      } else if (mPendingInstalls.size() > 0) {          HandlerParams params = mPendingInstalls.get(0);          if (params != null) {              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);                  }                  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 woek");                      mHandler.sendEmptyMessage(MCS_BOUND);                  }              }          }      } else {          // Should never happen ideally.          Slog.w(TAG, "Empty queue");      }      break;  }

HandlerParams params = mPendingInstalls.get(0)读取出我们要安装的包信息,然后清楚该包信息,如果还有其他包就继续发MCS_BOUND这个消息,循环,直到都安装完了。然后安装在哪里呢?也就是
params.startCopy()

这个了,进去看看
final boolean startCopy() {      boolean res;      try {          if (DEBUG_INSTALL) Slog.i(TAG, "startCopy");            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;      }      handleReturnCode();      return res;  }

这里的handleStartCopy()和handleServiceError()和handleReturnCode()都是abstract函数,希望大家还记得刚才我们发消息的时候携带的是InstallParams个类,InstallParams继承于HandlerParams,所以我们就会知道这三个abstract是在哪里实现的了,我们先说说handleStartCopy(),主要给两个变量完成了赋值工作也个是mArgs也就是InstallArgs,一个是ret标识是否安装成功的。handleServiceError()这个不讲解,然后看handleReturnCode()
@Override  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);      }  }

也就是processPendingInstall(mArgs, mRet)
private void processPendingInstall(final InstallArgs args, final int currentStatus) {      // Queue up an async operation since the package installation may take a little while.      mHandler.post(new Runnable() {          public void run() {              mHandler.removeCallbacks(this);               // Result object to be returned              PackageInstalledInfo res = new PackageInstalledInfo();              res.returnCode = currentStatus;              res.uid = -1;              res.pkg = null;              res.removedInfo = new PackageRemovedInfo();              if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {                  args.doPreInstall(res.returnCode);                  synchronized (mInstallLock) {                      installPackageLI(args, true, res);                  }                  args.doPostInstall(res.returnCode);              }                // A restore should be performed at this point if (a) the install              // succeeded, (b) the operation is not an update, and (c) the new              // package has a backupAgent defined.              final boolean update = res.removedInfo.removedPackage != null;              boolean doRestore = (!update                      && res.pkg != null                      && res.pkg.applicationInfo.backupAgentName != null);                // Set up the post-install work request bookkeeping.  This will be used              // and cleaned up by the post-install event handling regardless of whether              // there's a restore pass performed.  Token values are >= 1.              int token;              if (mNextInstallToken < 0) mNextInstallToken = 1;              token = mNextInstallToken++;                PostInstallData data = new PostInstallData(args, res);              mRunningInstalls.put(token, data);              if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {                  // Pass responsibility to the Backup Manager.  It will perform a                  // restore if appropriate, then pass responsibility back to the                  // Package Manager to run the post-install observer callbacks                  // and broadcasts.                  IBackupManager bm = IBackupManager.Stub.asInterface(                          ServiceManager.getService(Context.BACKUP_SERVICE));                  if (bm != null) {                      if (DEBUG_INSTALL) Log.v(TAG, "token " + token                              + " to BM for possible restore");                      try {                          bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);                      } catch (RemoteException e) {                          // can't happen; the backup manager is local                      } catch (Exception e) {                          Slog.e(TAG, "Exception trying to enqueue restore", e);                          doRestore = false;                      }                  } else {                      Slog.e(TAG, "Backup Manager not found!");                      doRestore = false;                  }              }                if (!doRestore) {                  // No restore possible, or the Backup Manager was mysteriously not                  // available -- just fire the post-install work request directly.                  if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);                  Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);                  mHandler.sendMessage(msg);              }          }      });  }

在这里启动了一个线程进行安装,也就是
PackageInstalledInfo res = new PackageInstalledInfo();  res.returnCode = currentStatus;  res.uid = -1;  res.pkg = null;  res.removedInfo = new PackageRemovedInfo();  if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {      args.doPreInstall(res.returnCode);      synchronized (mInstallLock) {          installPackageLI(args, true, res);      }      args.doPostInstall(res.returnCode);  }

也就是installPackageLI(args, true, res),这里代码较多,不再全部列出
if (replace) {      replacePackageLI(pkg, parseFlags, scanMode,              installerPackageName, res);  } else {      installNewPackageLI(pkg, parseFlags, scanMode,              installerPackageName,res);  }

很明显,如果是第一次安装走installNewPackageLI,如果是更新走replacePackageLI,我们去installNewPackageLI
/*  * Install a non-existing package.  */  private void installNewPackageLI(PackageParser.Package pkg,          int parseFlags,          int scanMode,          String installerPackageName, PackageInstalledInfo res) {      // Remember this for later, in case we need to rollback this install      String pkgName = pkg.packageName;        boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();      res.name = pkgName;      synchronized(mPackages) {          if (mSettings.mRenamedPackages.containsKey(pkgName)) {              // A package with the same name is already installed, though              // it has been renamed to an older name.  The package we              // are trying to install should be installed as an update to              // the existing one, but that has not been requested, so bail.              Slog.w(TAG, "Attempt to re-install " + pkgName                      + " without first uninstalling package running as "                      + mSettings.mRenamedPackages.get(pkgName));              res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;              return;          }          if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {              // Don't allow installation over an existing package with the same name.              Slog.w(TAG, "Attempt to re-install " + pkgName                      + " without first uninstalling.");              res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;              return;          }      }      mLastScanError = PackageManager.INSTALL_SUCCEEDED;      PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,              System.currentTimeMillis());      if (newPackage == null) {          Slog.w(TAG, "Package couldn't be installed in " + pkg.mPath);          if ((res.returnCode=mLastScanError) == PackageManager.INSTALL_SUCCEEDED) {              res.returnCode = PackageManager.INSTALL_FAILED_INVALID_APK;          }      } else {          updateSettingsLI(newPackage,                  installerPackageName,                  res);          // delete the partially installed application. the data directory will have to be          // restored if it was already existing          if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {              // remove package from internal structures.  Note that we want deletePackageX to              // delete the package data and cache directories that it created in              // scanPackageLocked, unless those directories existed before we even tried to              // install.              deletePackageLI(                      pkgName, false,                      dataDirExists ? PackageManager.DONT_DELETE_DATA : 0,                              res.removedInfo, true);          }      }  }

也就是
PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,          System.currentTimeMillis());

其他都是判断一下到目前位置是否符合安装条件,也就是PackageManager.INSTALL_SUCCEEDED是否成功,如果成功就继续安装,不成功就重新安装或者返回了。scanPackageLI是一个重构函数,一个首参数是PackageParser.Package,一个首参数是File,我们看第一种,由于scanPackageLI是我们安装包的主要过程,有八百多行,做了很多安装需要的工作,具体在安装时做了什么工作,有兴趣的可以研究一下,这里就不再一一列出。我们只看
int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,          pkg.applicationInfo.uid);

mInstaller也就是Installer,所以去看看
public int install(String name, int uid, int gid) {      StringBuilder builder = new StringBuilder("install");      builder.append(' ');      builder.append(name);      builder.append(' ');      builder.append(uid);      builder.append(' ');      builder.append(gid);      return execute(builder.toString());  }

execute也就是
private int execute(String cmd) {      String res = transaction(cmd);      try {          return Integer.parseInt(res);      } catch (NumberFormatException ex) {          return -1;      }  }

transaction也就是
private synchronized String transaction(String cmd) {      if (!connect()) {          Slog.e(TAG, "connection failed");          return "-1";      }        if (!writeCommand(cmd)) {          /*          * If installd died and restarted in the background (unlikely but          * possible) we'll fail on the next write (this one). Try to          * reconnect and write the command one more time before giving up.          */          Slog.e(TAG, "write command failed? reconnect!");          if (!connect() || !writeCommand(cmd)) {              return "-1";          }      }      if (LOCAL_DEBUG) {          Slog.i(TAG, "send: '" + cmd + "'");      }      if (readReply()) {          String s = new String(buf, 0, buflen);          if (LOCAL_DEBUG) {              Slog.i(TAG, "recv: '" + s + "'");          }          return s;      } else {          if (LOCAL_DEBUG) {              Slog.i(TAG, "fail");          }          return "-1";      }  }

writeCommand也就是
private boolean writeCommand(String _cmd) {      byte[] cmd = _cmd.getBytes();      int len = cmd.length;      if ((len < 1) || (len > 1024))          return false;      buf[0] = (byte) (len & 0xff);      buf[1] = (byte) ((len >> 8) & 0xff);      try {          mOut.write(buf, 0, 2);          mOut.write(cmd, 0, len);      } catch (IOException ex) {          Slog.e(TAG, "write error");          disconnect();          return false;      }      return true;  }

mOut是什么呢?
private boolean connect() {      if (mSocket != null) {          return true;      }      Slog.i(TAG, "connecting...");      try {          mSocket = new LocalSocket();            LocalSocketAddress address = new LocalSocketAddress("installd",                  LocalSocketAddress.Namespace.RESERVED);            mSocket.connect(address);            mIn = mSocket.getInputStream();          mOut = mSocket.getOutputStream();      } catch (IOException ex) {          disconnect();          return false;      }      return true;  }

真实面目,原来这里在用Socket进行通信,把我们要安装的包信息告诉服务器,让服务器来完成余下的工作,这个服务器在底层,完成了一些copy等工作,具体是什么不再深究

还是那句话给大师取乐,给后来者抛砖引玉,不要在背后骂我就谢天谢地了。

更多相关文章

  1. Android分别使用HTTP协议和TCP协议实现上传文件
  2. Android canvas.drawXXX参数问题
  3. android添加文件打开方式以及参数传递
  4. android使用Navigation实现Fragment之间的跳转之二:参数传递
  5. 在pc上安装android market软件并提取apk文件
  6. Android SDK Manager在添加Platforms和其他Packages时提示无法在
  7. android raw读取超过1M文件的方法

随机推荐

  1. 震惊了!每30秒学会一个Python小技巧,Github
  2. GitHub Actions,卧槽!牛批!
  3. 如何用 Python 给照片换色
  4. 原创丨我在 GitHub 上发现了哪些好的学习
  5. 还记得当年你是如何接触Python的吗?
  6. 我们终于可以把 bug 留给子孙后代了
  7. 牛逼了!Python代码补全利器,提高效率告别99
  8. 分析B站100万+视频,发现竟然有这么多干货
  9. 如何做出好看的可视化视频?这是我最舍不得
  10. 使用Flask在服务器实现一个API接口。