首页 > 代码库 > Kitkat的加密功能对应用做了什么?

Kitkat的加密功能对应用做了什么?

本文只分析手机加密后,启动到输入密码的界面的流程。

 

一. 加密后,系统服务针对加密功能做了什么?

 

最先启动的是SystemServer,调用ServerThread的initAndLoop()方法,开始启动系统的其他的服务。

在该文件中搜索“crypt”,得到如下内容:

1.

   private static final String ENCRYPTING_STATE = "trigger_restart_min_framework";

private static final String ENCRYPTED_STATE = "1";


从常量字符串的英文可以看出来是在手机加密完毕后重启进入最小framework系统的状态。

2.

            Slog.i(TAG, "Package Manager");

            // Only run "core" apps if we're encrypting the device.

            String cryptState = SystemProperties.get("vold.decrypt");

            if (ENCRYPTING_STATE.equals(cryptState)) {

                Slog.w(TAG, "Detected encryption in progress - only parsing core apps");

                onlyCore = true;

            } else if (ENCRYPTED_STATE.equals(cryptState)) {

                Slog.w(TAG, "Device encrypted - only parsing core apps");

                onlyCore = true;

            }



log中已经注明了,对于Package Manager来说,在加密状态下,只加载核心(core)应用,那么什么样的应用算是core应用呢?这也是本文重点关注的内容。我们在接下来的内容中进行探索。

 

接着看代码,vold.decrypt在第一次加密重启时,设置为” trigger_restart_min_framework“, 当加密完毕后重启时,就会被设置为”1”。此时onlyCore被设置为true。

3

.

            pm = PackageManagerService.main(context, installer,

                    factoryTest != SystemServer.FACTORY_TEST_OFF,

                    onlyCore);

            try {

                firstBoot = pm.isFirstBoot();

            } catch (RemoteException e) {

            }



调用PackageManagerService的静态方法main()开始创建PackageManagerService的对象,并做一些初始化工作。可以看到,main()方法的最后一个参数就是上一步中设置为true的onlyCore.看来应该是在main()方法中使用了onlyCore的值。

 

4. PackageManagerService.java

  public static final IPackageManager main(Context context, Installer installer,

            boolean factoryTest, boolean onlyCore) {

        PackageManagerService m = new PackageManagerService(context, installer,

                factoryTest, onlyCore);

        ServiceManager.addService("package", m);

        return m;

    }



main()方法中使用onlyCore传入了PackageManagerService的构造方法中。

 

接下来是对PackageManagerService中对于onlyCore的分析:

5.

   public PackageManagerService(Context context, Installer installer,

            boolean factoryTest, boolean onlyCore) {

       …….

        mOnlyCore = onlyCore;  

        ……        

mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),

                    mSdkVersion, mOnlyCore);

       …..

    

   }



首先给mOnlyCore赋值为onlyCore,这里为true,那么我们以后看到mOnlyCore和onlyCore都认为是ture就可以了。尽管Settings调用的readLPw()方法中传入了mOnlyCore,但是在该方法中并没有使用。

 

6. 接下来的内容,就是我们这里的重点了。

对于PackageManagerService来说,在构造方法中需要对已经安装的应用进行扫描,进行重新安装的操作,针对非正常关机等情况,对系统中的应用信息进行修复等操作。

Android手机安装应用的路径主要由以下代码所示:


private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay"

File frameworkDir = new File(Environment.getRootDirectory(), "framework");

File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");

File systemAppDir = new File(Environment.getRootDirectory(), "app");

File vendorAppDir = new File("/vendor/app");

上述代码标识了需要扫描的路径:

/vendor/overlay

/system/framework/

/system/priv-app

/system/app

/vender/app

对于这部分内容,值得说的是/system/priv-app目录是Kitkat以后才开始使用的,将一些以前放置到/system/app目录下的系统应用放置到该目录下。没有看到有什么特别大的区别。

 

7. 需要扫描以上路径中的安装文件,然后解析文件,进行安装操作。都会调用一个Pms的一个非常重要方法scanDirLI(),如接下来的代码所示(由于太长,都是一个模式,我们拿出一个来分析,这里选择/system/app目录):

 

 

            // Collect ordinary system packages.

            File systemAppDir = new File(Environment.getRootDirectory(), "app");

            mSystemInstallObserver = new AppDirObserver(

                systemAppDir.getPath(), OBSERVER_EVENTS, true, false);

            mSystemInstallObserver.startWatching();

            scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM

                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

 


首先创建/system/app目录,当然如果已经创建了,就获得该文件对象,然后设置一个AppDirObserver,也就是当该目录下的apk应用变更的时候,就会触发这个监听,让后进行一系列的重新安装操作。

最明显的就是,我们编译的系统应用,直接adb push xxx.apk/system/app 操作的时候,被替换的xxx.apk就会发生变化,此时就会触发这个AppDirObserver.接下来mSystemInstallObserver监听器的startWatching()方法,开始监听。

终于到了关键方法scanDirLI()了,它就是真正扫描这个目录,并解析出apk包,并安装的执行者。这里出入的参数可以看出PackageParser.PARSE_IS_SYSTEM|PackageParser.PARSE_IS_SYSTEM_DIR,是系统应用和系统目录,我们知道/system/app目录下的都是系统应用,第三方的应用是不能安装到该目录下的。

 

8.我们先从PackageManagerService的构造方法中出来,进入到scanDirLI()方法中:

private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {

        String[] files = dir.list();

        ……

        for (i=0; i<files.length; i++) {

            File file = new File(dir, files[i]);

            ……

            PackageParser.Package pkg = scanPackageLI(file,

                    flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);

            …..

}

}



该方法中,获得传入的参数dir,得到该目录下的文件列表,也就是apk的文件列表,然后遍历该文件列表,调用scanPackageLI()方法,将apk解析成一个个的PackageParser.Package对象。

 

9.接着看scanPackageLI()方法中做了些什么?

需要注意的是,Pms中有两个同名的scanPackageLI()方法,签名不同。我们这里是调用第一个参数是File类型的scanPackageLI()方法。

private PackageParser.Package scanPackageLI(File scanFile,

            int parseFlags, int scanMode, long currentTime, UserHandle user) {

        mLastScanError = PackageManager.INSTALL_SUCCEEDED;

        String scanPath = scanFile.getPath();

        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath);

        parseFlags |= mDefParseFlags;

        PackageParser pp = new PackageParser(scanPath);

        pp.setSeparateProcesses(mSeparateProcesses);

        pp.setOnlyCoreApps(mOnlyCore);

        final PackageParser.Package pkg = pp.parsePackage(scanFile,

                scanPath, mMetrics, parseFlags, (scanMode & SCAN_TRUSTED_OVERLAY) != 0);

}


我们看到红色代码中使用了mOnlyCore,是给PackageParser类型的pp设置的,接着调用pp的parsePacakge()方法开始解析apk安装包了。

 

10.

public Package parsePackage(File sourceFile, String destCodePath,

            DisplayMetrics metrics, int flags, boolean trustedOverlay) {

        …….

try {

            // XXXX todo: need to figure out correct configuration.

            pkg = parsePackage(res, parser, flags, trustedOverlay, errorText);

        } catch (Exception e) {

            errorException = e;

            mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;

        }

        …….

}


调用同名方法parsePackage(),第一个参数是Resources。我们接着看该方法的内容:

 

11.

   private Package parsePackage(

        Resources res, XmlResourceParser parser, int flags, boolean trustedOverlay,

        String[] outError) throws XmlPullParserException, IOException {

        AttributeSet attrs = parser;

        ……

        if (mOnlyCoreApps) {

            boolean core = attrs.getAttributeBooleanValue(null, "coreApp", false);

            if (!core) {

                mParseError = PackageManager.INSTALL_SUCCEEDED;

                return null;

            }

        }

        ……

    }


终于到正题了,这里看到如果mOnlyCoreApps为true的时候,然后获取apk应用的一个名字为“coreApp”的属性的值,该值是一个boolean值,如果没有设置该属性值,这里给了一个默认值false。

这里本来就是行扫描AndroidManifest.xml中的属性值,所以”coreApp”也是在AndroidManifest.xml中设置的

 


图1 kitkat工程中AndroidManifest.xml中设置’coreApp”属性的apk目录

 

从上图中可以看出是“coreApp”的apk有framework-res.apk,DefaultContainerService.apk(存储相关), FakeOemFeatures.apk,InputDevices.apk, Keyguard.apk(锁屏),ProxyHandler.apk,SettingsProvider.pak,Shell.apk,SystemUI.apk,Dialer.apk(由于InCallUi会被编译到Dialer.apk中),Settings.apk,LatinIME.apk, TelephonyProvider.apk, TeleService.apk.移动有15个coreApp。

但是实际的项目中,可能没有FakeOemFeatures.apk,该apk应该是最新添加的。

除了这15个应用以外其他的,应用core为false,进入if 的true分支,设置mParseError 为PackageManager.INSTALL_SUCCEEDED,并返回null,也就是说,当在onlyCore的模式下,非coreApp直接设置状态为已经解析完毕,然后返回null,表示该apk中没有解析出内容。

 

这里也就解释了,为什么在加密后,进入解锁界面的时候,仅仅有部分应用是可用的。

 

从这里也可以看出,在此界面,只能使用Latin输入法,可以打电话(Dialer),可以挂载存储设备。设置中的功能还是可用的。因为解锁的界面就属于Settings.apk,类名称是CryptKeeper。

至此,我们已经对Android加密功能对Android应用到底做了些那些区别对待。


Kitkat的加密功能对应用做了什么?