Android App安装弹窗表现流程

计算机软件开发 2024-9-30 11:07:03 49 0 来自 中国
一、APP的安装

1、常见安装方式


  • 系统应用和预制应用安装――开机时完成,没有安装界面,在PKMS的构造函数中完成安装
  • 网络下载或第三方应用安装――调用PackageManager.installPackages(),有安装界面。
  • ADB工具安装――没有安装界面,它通过启动pm脚本的情势,然后调用com.android.commands.pm.Pm类,之后调用到PMS.installStage()完成安装。

    1.png
2、APK的署名校验明确

V1署名apk-signature-v1-location.png只是校验了apk资源,并没有束缚zip,署名信息存储在zip/META-INF中。v2署名是一个对全文件举行署名的方案,能提供更快的应用安装时间、对未授权APK文件的更改提供更多掩护.3、APK安装过程


  • 开机后扫描应用安装目次和系统App目次,剖析此中的apk文件将相干信息加载到PKMS中的数据结构中,同时对于没有对应数据目次的App天生对应的数据目次
  • 注册包名App等信息、以及相干的四大组件到PMS中
  • 将剖析到的数据同步到/data/system/packages.xml中
4、App安装涉及的目次明确


  • 系统App安装目次
1、 /system/app:  Android系统App路径
2、/system/priv-app: 同上,但比/system/app权限优先级更高,可以拿到ApplicationInfo.PRIVATE_FLAG_PRIVILEGED特殊权限
3、/vendor/app: odm大概oem厂商预制系统App目次
4、/vendor/priva-app: 同上


  • 平常应用App安装目次
/data/app:用户App步伐安装的目次。安装时Apk会被拷贝至此目次


  • 用户数据目次
/data/data:存放应用步伐的数据,无论是系统App还是平常App,App产生的用户数据都存放在/data/data/包名/目次下。


  • App注册表目次
/data/system
1、packages.xml:
记载apk的permissions,,flags,ts,version,uesrid等信息,这些信息紧张通apk的AndroidManifest.xml剖析获取,当系统举行步伐安装、卸载和更新等利用时,均会更新该文件。
2、packages-backup.xml : 备份文件
3、packages-stopped.xml : 记载被用户强行制止的应用的Package信息
4、packages-stopped-backup.xml : pakcages-stoped.xml文件的备份
5、packages.list : 记载非系统自带的APK的数据信息,这些APK有变革时会更新该文件
5、package.xml文件剖析

<?xml version='1.0' encoding='utf-8' standalone='yes' ?><packages>    <version sdkVersion="xxx" databaseVersion="xxx" fingerprint="xxx" />    <version volumeUuid="xxx" sdkVersion="xxx" databaseVersion="xxx" fingerprint="xxx" />    <permissions>        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />        ...    </permissions>         <package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">        <sigs count="1">            <cert index="1" key="xxx" />        </sigs>        <perms>            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />            ...        </perms>        <proper-signing-keyset identifier="1" />    </package>      ...               <updated-package name="xxx.xxx.xxx" codePath="/system/app/xxx" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="11" nativeLibraryPath="/system/app/xxx/lib" primaryCpuAbi="armeabi-v7a" sharedUserId="1000" />    <shared-user name="android.media" userId="10005">        <sigs count="1">            <cert index="2" />        </sigs>        <perms>            <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />            ...        </perms>    </shared-user>    ...</packages>   package.xml对应的类图关系



  • BasePermission
BasePermission对应packages.xml中permissions标签的子标签item,对于上述所界说的每一项权限都会天生一个BasePermission。
protection :品级分为四个
1、平常权限(normal)
2、运行时权限(dangerous)
3、署名权限(signature)
4、特殊权限(privileged)
    <permissions>        <item name="android.permission.REAL_GET_TASKS" package="android" protection="18" />        ...    <permissions/>  

  • PermissionsState

    3.png
PermissionState对应的是<package>标签中的子标签<perms>标签中的内容
<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">        <perms>            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />            ...        </perms></package>

  • PackageSignatures
PackageSignatures对应的是<package>标签中的子标签<sigs>标签中的内容
<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">        <sigs count="1">            <cert index="1" key="xxx" />        </sigs></package>

  • PackageSetting
PackageSetting这个数据结构类是packages.xml里面记载安装包信息标签<package>相对应的类,可以看到PackageSetting继承了PackageSettingBase类,PackageSettingBase类继承自SettingBase类。应用的根本信息生存在PackageSettingBase类的成员变量中,署名则生存在PackageSignatures中,权限状态生存在父类的SettingBase的PermissionsState中。
<package name="com.android.providers.telephony" codePath="/system/priv-app/TelephonyProvider" nativeLibraryPath="/system/priv-app/TelephonyProvider/lib" publicFlags="1007402501" privateFlags="8" ft="11e8dc5d800" it="11e8dc5d800" ut="11e8dc5d800" version="25" versionName="7.1.2" applicationName="电话和短信存储" sharedUserId="1001" isOrphaned="true">        <sigs count="1">            <cert index="1" key="xxx" />        </sigs>        <perms>            <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />            ...        </perms>        <proper-signing-keyset identifier="1" /></package>  

  • SharedUserSetting
SharedUserSetting这个数据结构类是packages.xml里面记载安装包信息标签<shared-user>相对应的类,它和PackageSetting有一个共同的父类即SettingBase,即都是通过父类的PermissionsState来生存权限信息。SharedUserSetting被筹划的用途紧张用来形貌具有雷同的sharedUserId的应用信息,它的成员变量packages生存了全部具有雷同sharedUserId的应用信息引用,而成员变量userId则是记载多个APK共享的UID。共享用户的应用的署名是雷同的,署名生存在成员变量signatures中(这里有一点必要注意,由于署名雷同,Android运行时很轻易检索到某个应用拥有雷同的sharedUserId的其他应用)。

<shared-user name="android.media" userId="10005">        <sigs count="1">            <cert index="2" />        </sigs>        <perms>            <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />            ...        </perms></shared-user>

  • Settings : package.xml 终极大管家类

    5.png
二、APP安装团体流程

代码堆栈:http://androidxref.com/9.0.0_r3/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
1、安装APP代码入口

    <activity android:name=".InstallStart"    android:exported="true"    android:excludeFromRecents="true">    <intent-filter android:priority="1">        <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="file"/>        <data android:scheme="content"/>        <data android:mimeType="application/vnd.android.package-archive"/>    </intent-filter>    <intent-filter android:priority="1">        <action android:name="android.intent.action.INSTALL_PACKAGE"/>        <category android:name="android.intent.category.DEFAULT"/>        <data android:scheme="file"/>        <data android:scheme="package"/>        <data android:scheme="content"/>    </intent-filter>    <intent-filter android:priority="1">        <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS"/>        <category android:name="android.intent.category.DEFAULT"/>    </intent-filter>    </activity>2、根据Uri的Scheme协议差别,跳转到差别的界面

content协议跳转到InstallStaging,package协议跳转到PackageInstallerActivity
protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ......        Intent nextActivity = new Intent(intent);        nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);        // The the installation source as the nextActivity thinks this activity is the source, hence        // set the originating UID and sourceInfo explicitly        nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);        nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);        nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);        //1、content的Uri协议 : InstallStaging         //2、package的Url协议:PackageInstallerActivity         if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {            nextActivity.setClass(this, PackageInstallerActivity.class);        } else {            Uri packageUri = intent.getData();            if (packageUri != null && (packageUri.getScheme().equals(ContentResolver.SCHEME_FILE)                    || packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))) {                // Copy file to prevent it from being changed underneath this process                   //1、content的Uri协议 : InstallStaging                 nextActivity.setClass(this, InstallStaging.class);            } else if (packageUri != null && packageUri.getScheme().equals(                    PackageInstallerActivity.SCHEME_PACKAGE)) {                //package的Url协议:PackageInstallerActivity                 nextActivity.setClass(this, PackageInstallerActivity.class);            } 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();    }3、InstallStaging类的先容

紧张内容:将content协议的Uri转换为package协议的Uri,然后通过IO情势写入到mStagedFile文件中
作用:紧张起了转换的作用,将content协议的Uri转换为package协议,然后跳转到PackageInstallerActivity
@Override  protected void onResume() {      super.onResume();      if (mStagingTask == null) {          if (mStagedFile == null) {              try {                  mStagedFile = TemporaryFileManager.getStagedFile(this);              } catch (IOException e) {                  showError();                  return;              }          }          mStagingTask = new 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)) {                if (in == null) {                    return false;                }                try (OutputStream out = new FileOutputStream(mStagedFile)) {                    byte[] buffer = new byte[4096];                    int bytesRead;                    while ((bytesRead = in.read(buffer)) >= 0) {                        if (isCancelled()) {                            return false;                        }                        out.write(buffer, 0, bytesRead);                    }                }            } catch (IOException | SecurityException e) {                Log.w(LOG_TAG, "Error staging apk from content URI", e);                return false;            }            return true;        }        @Override        protected void onPostExecute(Boolean success) {           if (session != null) {         Intent broadcastIntent = new Intent(BROADCAST_ACTION);         broadcastIntent.setPackage(                 getPackageManager().getPermissionControllerPackageName());         broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);         PendingIntent pendingIntent = PendingIntent.getBroadcast(                 InstallInstalling.this,                 mInstallId,                 broadcastIntent,                 PendingIntent.FLAG_UPDATE_CURRENT);        //APP安装的启动入口         session.commit(pendingIntent.getIntentSender());         mCancelButton.setEnabled(false);         setFinishOnTouchOutside(false);     } else {         getPackageManager().getPackageInstaller().abandonSession(mSessionId);         if (!isCancelled()) {             launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);         }     }}4、PackageInstallerActivity类的先容


  • 它就是在安装应用表现弹窗的Activity
@Overrideprotected void onCreate(Bundle icicle) {    super.onCreate(icicle);    if (icicle != null) {        mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);    }    mPm = getPackageManager();    mIpm = AppGlobals.getPackageManager();    mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);    mInstaller = mPm.getPackageInstaller();    mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);    ...    //根据Uri的Scheme举行预处理处罚    boolean wasSetUp = processPackageUri(packageUri);    if (!wasSetUp) {        return;    }    bindUi(R.layout.install_confirm, false);    //判定是否是未知泉源的应用,假如开启答应安装未知泉源选项则直接初始化安装    checkIfAllowedAndInitiateInstall();}

  • 分别对content和package两种差别协议处理处罚
private boolean processPackageUri(final Uri packageUri) {     mPackageURI = packageUri;     final String scheme = packageUri.getScheme();//1     switch (scheme) {         case SCHEME_PACKAGE: {             try {              ...         } break;         case SCHEME_FILE: {             File sourceFile = new File(packageUri.getPath());             //得到sourceFile的包信息             PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);             if (parsed == null) {                 Log.w(TAG, "arse error when parsing manifest. Discontinuing installation");                 showDialogInner(DLG_PACKAGE_ERROR);                 setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);                 return false;             }             //对parsed举行进一步处理处罚得到包信息PackageInfo             mPkgInfo = PackageParser.generatePackageInfo(parsed, null,                     PackageManager.GET_PERMISSIONS, 0, 0, null,                     new PackageUserState());//3             mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);         } break;         default: {             Log.w(TAG, "Unsupported scheme " + scheme);             setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);             finish();             return false;         }     }     return true; }

  • 弹窗上表现是否优劣法安装的处理处罚
private void checkIfAllowedAndInitiateInstall() {       //判定假如答应安装未知泉源大概根据Intent判定得出该APK不是未知泉源       if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {           //初始化安装           initiateInstall();           return;       }       // 假如管理员限定来自未知源的安装, 就弹出提示Dialog大概跳转到设置界面       if (isUnknownSourcesDisallowed()) {           if ((mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,                   Process.myUserHandle()) & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {                   showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);               return;           } else {               startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));               finish();           }       } else {           handleUnknownSources();       }   }

  • InstallStaging.java   session.commit() 去实验系统framework层
protected void onPostExecute(Boolean success) {           if (session != null) {         Intent broadcastIntent = new Intent(BROADCAST_ACTION);         broadcastIntent.setPackage(                 getPackageManager().getPermissionControllerPackageName());         broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);         PendingIntent pendingIntent = PendingIntent.getBroadcast(                 InstallInstalling.this,                 mInstallId,                 broadcastIntent,                 PendingIntent.FLAG_UPDATE_CURRENT);        //APP安装的启动入口         session.commit(pendingIntent.getIntentSender());         mCancelButton.setEnabled(false);         setFinishOnTouchOutside(false);     } else {         getPackageManager().getPackageInstaller().abandonSession(mSessionId);         if (!isCancelled()) {             launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);         }     }

  • PackageInstaller.java  类
public void commit(@NonNull IntentSender statusReceiver) {           try {               mSession.commit(statusReceiver);           } catch (RemoteException e) {               throw e.rethrowFromSystemServer();           }       }

  • PackageInstallerSession.java类
    PackageInstallObserverAdapter继承PackageInstallObserver  :  监听安装APP的过程
    mSessionId是安装包的会话id,mInstallId是期待的安装变乱id
@Override   public void commit(IntentSender statusReceiver) {       Preconditions.checkNotNull(statusReceiver);       ...       mActiveCount.incrementAndGet();       final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,               statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);      //Handler发送一个范例为MSG_COMMIT的消息,关照PMS安装应用       mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();   }private final Handler.Callback mHandlerCallback = new Handler.Callback() {      @Override      public boolean handleMessage(Message msg) {          final PackageInfo pkgInfo = mPm.getPackageInfo(                  params.appPackageName, PackageManager.GET_SIGNATURES                          | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);          final ApplicationInfo appInfo = mPm.getApplicationInfo(                  params.appPackageName, 0, userId);          synchronized (mLock) {              if (msg.obj != null) {                  mRemoteObserver = (IPackageInstallObserver2) msg.obj;              }              try {                  //PMS开始安装应用                  commitLocked(pkgInfo, appInfo);              } catch (PackageManagerException e) {                  final String completeMsg = ExceptionUtils.getCompleteMessage(e);                  Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);                  destroyInternal();                   //安装时间出现非常标题                  dispatchSessionFinished(e.error, completeMsg, null);              }              return true;          }      }  };private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)          throws PackageManagerException {     ...    //关照 PMS开始安装应用      mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,              installerPackageName, installerUid, user, mCertificates);  }总结:

  • 根据Uri的Scheme协议差别,跳转到差别的界面,content协议跳转到InstallStaging,package跳转到PackageInstallerActivity。
  • InstallStaging将content协议的Uri转换为File协议,然后跳转到PackageInstallerActivity。
  • PackageInstallerActivity会分别对package协媾和file协议的Uri举行处理处罚,假如是file协议会剖析APK文件得到包信息PackageInfo。
  • PackageInstallerActivity中会对未知泉源举行处理处罚,假如答应安装未知泉源大概根据Intent判定得出该APK不是未知泉源,就会初始化安装确认界面,假如管理员限定来自未知源的安装, 就弹出提示Dialog大概跳转到设置界面。
参考链接:https://maoao530.github.io/2017/01/18/package-install/
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-23 17:32, Processed in 0.175814 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表