Android 应用程序管理机制

fmms 13年前
     <p>         android应用程序管理主要由PackageManager这个类来管理,实现PackageManager这个抽象类的是ContextImpl.java。在ContextImpl.java中,有一个内部静态类叫ApplicationPackageManager,实现了所有PackageManager的接口。</p>    <p></p>    <pre class="brush:java; toolbar: true; auto-links: false;"> static final class ApplicationPackageManager extends PackageManager {  ..........  }</pre>    <p>ApplicationPackageManager又是通过对IPackageManager封装调用,来实现的。</p>    <p></p>    <pre class="brush:java; toolbar: true; auto-links: false;"> public PackageInfo getPackageInfo(String packageName, int flags)                 throws NameNotFoundException {             try {                 PackageInfo pi = mPM.getPackageInfo(packageName, flags);                 if (pi != null) {                     return pi;                 }             } catch (RemoteException e) {                 throw new RuntimeException("Package manager has died", e);             }              throw new NameNotFoundException(packageName);         }</pre>    <p>这里的mPM就是IPackageManager,PackageManagerService就是对IPackageManager的实现。所以我们平时对PackageManager的调用,最终是的在PackageManagerService.java中实现的。在PackageManagerService.java每个方法的实现。</p>    <p><strong>PackageManagerService启动流程:</strong></p>    <p></p>    <p></p>    <p>PackageManagerService(context, factoryTest)<span style="font-family:宋体;">是包管理服务的主进程。它完成了对</span><span style="font-family:Times New Roman;">/system/app,/data/app,/system/framework,/data/app-private</span><span style="font-family:宋体;">下的</span><span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">文件的解析。详细流程如下:</span></p>    <p>1.<span style="font-family:宋体;">建立</span><span style="font-family:Times New Roman;">java</span><span style="font-family:宋体;">层的</span><span style="font-family:Times New Roman;">installer</span><span style="font-family:宋体;">与</span><span style="font-family:Times New Roman;">c</span><span style="font-family:宋体;">层的</span><span style="font-family:Times New Roman;">installd</span><span style="font-family:宋体;">的</span><span style="font-family:Times New Roman;">socket</span><span style="font-family:宋体;">联接,使得在上层的</span><span style="font-family:Times New Roman;">install,remove,dexopt</span><span style="font-family:宋体;">等功能最终由</span><span style="font-family:Times New Roman;">installd</span><span style="font-family:宋体;">在底层实现</span></p>    <p>2.<span style="font-family:宋体;">建立</span><span style="font-family:Times New Roman;">PackageHandler</span><span style="font-family:宋体;">消息循环,用于处理外部的</span><span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">安装请求消息,如</span><span style="font-family:Times New Roman;">adb install,packageinstaller</span><span style="font-family:宋体;">安装</span><span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">时会发送消息</span></p>    <p>3.<span style="font-family:宋体;">解析</span><span style="font-family:Times New Roman;">/system/etc/permission</span><span style="font-family:宋体;">下</span><span style="font-family:Times New Roman;">xml</span><span style="font-family:宋体;">文件</span><span style="font-family:Times New Roman;">(framework/base/data/etc/)</span><span style="font-family:宋体;">,包括</span><span style="font-family:Times New Roman;">platform.xml</span><span style="font-family:宋体;">和系统支持的各种硬件模块的</span><span style="font-family:Times New Roman;">feature.</span><span style="font-family:宋体;">主要工作:</span></p>    <p>(1)<span style="font-family:宋体;">建立底层</span><span style="font-family:Times New Roman;">user ids</span><span style="font-family:宋体;">和</span><span style="font-family:Times New Roman;">group ids </span><span style="font-family:宋体;">同上层</span><span style="font-family:Times New Roman;">permissions</span><span style="font-family:宋体;">之间的映射;可以指定一个权限与几个组</span><span style="font-family:Times New Roman;">ID</span><span style="font-family:宋体;">对应。当一个</span><span style="font-family:Times New Roman;">APK</span><span style="font-family:宋体;">被授予这个权限时,它也同时属于这几个组。</span></p>    <p>(2)<span style="font-family:宋体;">给一些底层用户分配权限,如给</span><span style="font-family:Times New Roman;">shell</span><span style="font-family:宋体;">授予各种</span><span style="font-family:Times New Roman;">permission</span><span style="font-family:宋体;">权限;把一个权限赋予一个</span><span style="font-family:Times New Roman;">UID</span><span style="font-family:宋体;">,当进程使用这个</span><span style="font-family:Times New Roman;">UID</span><span style="font-family:宋体;">运行时,就具备了这个权限。</span></p>    <p>(3) library,<span style="font-family:宋体;">系统增加的一些应用需要</span><span style="font-family:Times New Roman;">link</span><span style="font-family:宋体;">的扩展</span><span style="font-family:Times New Roman;">jar</span><span style="font-family:宋体;">库;</span></p>    <p>(4) feature,<span style="font-family:宋体;">系统每增加一个硬件,都要添加相应的</span><span style="font-family:Times New Roman;">feature.</span><span style="font-family:宋体;">将解析结果放入</span><span style="font-family:Times New Roman;">mSystemPermissions,mSharedLibraries,mSettings.mPermissions,mAvailableFeatures</span><span style="font-family:宋体;">等几个集合中供系统查询和权限配置使用。</span></p>    <p>4.<span style="font-family:宋体;">检查</span><span style="font-family:Times New Roman;">/data/system/packages.xml</span><span style="font-family:宋体;">是否存在,这个文件是在解析</span><span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">时由</span></p>    <p>writeLP()<span style="font-family:宋体;">创建的,里面记录了系统的</span>permissions,以及每个<span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">的</span>name,codePath,flags,version,uesrid等信息,这些信息主要通过<span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">的</span></p>    <p>AndroidManifest.xml<span style="font-family:宋体;">解析获取,解析完</span><span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">后将更新信息写入这个文件并保</span></p>    <p>存到<span style="font-family:Times New Roman;">flash</span><span style="font-family:宋体;">,下次开机直接从里面读取相关信息添加到内存相关列表中。当有</span><span style="font-family:Times New Roman;">apk</span></p>    <p>升级,安装或删除时会更新这个文件。</p>    <p>5.<span style="font-family:宋体;">检查</span><span style="font-family:Times New Roman;">BootClassPath</span><span style="font-family:宋体;">,</span><span style="font-family:Times New Roman;">mSharedLibraries</span><span style="font-family:宋体;">及</span><span style="font-family:Times New Roman;">/system/framework</span><span style="font-family:宋体;">下的</span><span style="font-family:Times New Roman;">jar</span></p>    <p>是否需要<span style="font-family:Times New Roman;">dexopt</span><span style="font-family:宋体;">,需要的则通过</span><span style="font-family:Times New Roman;">dexopt</span><span style="font-family:宋体;">进行优化</span></p>    <p>6.<span style="font-family:宋体;">启动</span><span style="font-family:Times New Roman;">AppDirObserver</span><span style="font-family:宋体;">线程监测</span><span style="font-family:Times New Roman;">/system/framework,/system/app,/data/app,/data/</span></p>    <p>app-private<span style="font-family:宋体;">目录的事件</span><span style="font-family:Times New Roman;">,</span><span style="font-family:宋体;">主要监听</span><span style="font-family:Times New Roman;">add</span><span style="font-family:宋体;">和</span><span style="font-family:Times New Roman;">remove</span><span style="font-family:宋体;">事件。对于目录监听底层通过</span></p>    <p>inotify<span style="font-family:宋体;">机制实现,</span><span style="font-family:Times New Roman;">inotify </span><span style="font-family:宋体;">是一种文件系统的变化通知机制,如文件增加、删除</span></p>    <p>等事件可以立刻让用户态得知<span style="font-family:Times New Roman;">,</span><span style="font-family:宋体;">它为用户态监视文件系统的变化提供了强大的支持。</span></p>    <p>当有<span style="font-family:Times New Roman;">add event</span><span style="font-family:宋体;">时调用</span><span style="font-family:Times New Roman;">scanPackageLI(File , int , int)</span><span style="font-family:宋体;">处理;</span></p>    <p>当有<span style="font-family:Times New Roman;">remove event</span><span style="font-family:宋体;">时调用</span><span style="font-family:Times New Roman;">removePackageLI()</span><span style="font-family:宋体;">处理</span><span style="font-family:Times New Roman;">;</span></p>    <p>7.<span style="font-family:宋体;">对于以上几个目录下的</span><span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">逐个解析,主要是解析每个</span><span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">的</span><span style="font-family:Times New Roman;">AndroidMa-</span></p>    <p>nifest.xml<span style="font-family:宋体;">文件,处理</span><span style="font-family:Times New Roman;">asset/res</span><span style="font-family:宋体;">等资源文件,建立起每个</span><span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">的配置结构信息,</span></p>    <p>并将每个<span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">的配置信息添加到全局列表进行管理。</span>f</p>    <p>8.<span style="font-family:宋体;">将解析的每个</span><span style="font-family:Times New Roman;">apk</span><span style="font-family:宋体;">的信息保存到</span><span style="font-family:Times New Roman;">packages.xml</span><span style="font-family:宋体;">和</span><span style="font-family:Times New Roman;">packages.list</span><span style="font-family:宋体;">文件里,</span></p>    <p>packages.list<span style="font-family:宋体;">记录了如下数据:</span><span style="font-family:Times New Roman;">pkgName</span><span style="font-family:宋体;">,</span><span style="font-family:Times New Roman;">userId</span><span style="font-family:宋体;">,</span><span style="font-family:Times New Roman;">debugFlag</span><span style="font-family:宋体;">,</span><span style="font-family:Times New Roman;">dataPath</span><span style="font-family:宋体;">(包的数据路径)</span></p>    <p><strong>dexopt:</strong></p>       如果我们想要求运行时的性能有进一步提高,就仍然需要对DEX文件进行进一步优化。优化DEX会产生一个可以快速载入执行的classes.dex文件,会进行包括byte-swapping,structure realigning与basic structure checks,更新ODEX header ,为了确保产生ODEX流程的正确性,Android提供了一个dexopt工具,用来做为虚拟机的辅助工具,可以在系統启动时,透過Dalvik虚拟机對載入的DEX文件執行佳化操作。    <br />      <br />    <p>优化发生的时机有两个: </p>    <p>对于预置应用,可以在系统编译后,生成优化文件,以ODEX 结尾。这样在发布时除</p> APK 文件(不包含 DEX)以外,还有一个相应的 ODEX 文件;    <br />   对于非预置应用,包含在 APK 文件里的 DEX 文件会在运行时通过dexopt进行优化,优化后的文件将被保存在缓存中(data/dalvik-cache)。       <br />    <br />    <p><strong>android安全机制概述</strong>:</p>    <p>      Android是一个权限分离的系统 ,这是利用Linux已有的权限管理机制,通过为每一个Application分配不同的uid和gid, 从而使得不同的Application之间的私有数据和访问(native以及java层通过这种sandbox机制)达到隔离的目的 。与此同时,Android还在此基础上进行扩展,提供了permission机制,它主要是用来对Application可以执行的某些具体操作进行权限细分和访问控制,同时提供了per-URI permission机制,用来提供对某些特定的数据块进行访问。<br />        Application 级别通过user ID和group Id实现安全控制;component级别通过permission来限制对于某一组件的访问;在data级别通过基于permission的per URI进行安全控制。</p>    <p><strong> uid  gid  gids</strong>:</p>        Android 的权限分离的基础是建立在 Linux 已有的 uid 、 gid 、 gids 基础上的 。    <br /> UID :Android 在 安装一个应用程序,就会为 它 分配一个 uid 。其中普通 Android 应用程序的 uid 是从 10000 开始分配 (Process.FIRST_APPLICATION_UID ), 10000 以下是系统进程的 uid 。    <br /> GID :对 于普通应用程序来说, gid 等于 uid 。由于每个应用程序的 uid 和 gid 都不相同, 因此不管是 native 层还是 java 层都能够达到保护私有数据的作用 。    <br /> GIDS : gids 是由框架在 Application 安装过程中生成,与 Application 申请的具体权限相关。 如果 Application 申请的相应的 permission 被 granted ,而且中有对应的 gids , 那么这个Application 的 gids 中将包含这个gids 。    <br />    <br />    <p><strong>installer类:</strong></p> 构造方法中,首先会进行一些成员变量的初始化,比如mContext, mFactoryTest, mMetrics, mSettings等。    <br /> 最重要的是初始化mInstaller这个变量:    <p></p>    <p></p>    <pre class="brush:java; toolbar: true; auto-links: false;">  Installer installer = new Installer();         if (installer.ping() && Process.supportsProcesses()) {             mInstaller = installer;         } else {             mInstaller = null;         }</pre>    <p>Installer这个是PackageManager与底层C模块进行通信的工具类,同socket进行通信,PackageManager所有对apk的安装,卸载等操作都是通过Installer进行的。对Installer的调用首先会调用ping()来判断socket是否已经连接。</p>    <p></p>    <p>连接方法:connect:</p>    <p></p>    <pre class="brush:java; toolbar: true; auto-links: false;"> 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;     }</pre>transaction首先会判断连接,如果socket连接正常,就将cmd命令写入socket文件,并且接收返回信息,并且返回给execute:    <br />    <pre class="brush:java; toolbar: true; auto-links: false;">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";             }         } //        Slog.i(TAG,"send: '"+cmd+"'");         if (readReply()) {             String s = new String(buf, 0, buflen); //            Slog.i(TAG,"recv: '"+s+"'");             return s;         } else { //            Slog.i(TAG,"fail");             return "-1";         }      }</pre>    <p> </p>    <p>执行命令的方法:execute(String cmd):</p>      <pre class="brush:java; toolbar: true; auto-links: false;">  private int execute(String cmd) {         String res = transaction(cmd);         try {             return Integer.parseInt(res);         } catch (NumberFormatException ex) {             return -1;         }     }</pre>install()方法:    <br />    <pre class="brush:java; toolbar: true; auto-links: false;">    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());     }</pre>    <br /> 其他方法:    <p></p>    <p>(1)  dexopt(String apkPath, int uid, boolean isPublic);优化dex文件<br /> (2)movedex(String srcPath, String dstPath); 移动dex文件<br /> (3)rmdex(String codePath);删除dex文件<br /> (4)remove(String name);移动apk<br /> (5)rename(String oldname, String newname);重命名<br /> (6)deleteCacheFiles(String name);删除cache文件<br /> (7)clearUserData(String name);删除user data<br /> (8)freeCache(long freeStorageSize);释放cache空间<br /> (9)setForwardLockPerm(String packagePathSuffix, int gid) 为apk文件增加前缀<br /> (10)getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath, PackageStats pStats)  获取apk信息<br /> (11) moveFiles();移动文件。</p>