Android動的配置2:APKインストールおよびAndroid Manifest.xml解析プロセス分析


転載は出典を明記してください.http://blog.csdn.net/ximsfei/article/details/50886134
githubアドレス:https://github.com/ximsfei/DynamicDeploymentApk前の記事:Android動的導入:GoogleオリジナルSplit APKの概要では、GoogleがSplitApkを実現するメカニズムを簡単に説明しています.次に、apkをインストールしない動的ロードを手動で実現するための一歩一歩の実践を始めます.まず、APK解析インストールのソースコードを理解します.adbコマンドでAndroid携帯電話にapkをインストールすることができます.
1. adb push xxx /data/app
     /data/app          ,    /system/app:    ,/vendor/app:      。
2. adb install xxx
3. adb shell pm install yyy

このうちadb pushコマンドは携帯電話を再起動する必要があり、xxxはホストコンピュータのapkパス、yyyはAndroid携帯電話のパスである.
adb pushを使用してapkをインストールする
ソース解析:adb pushコマンドでAPKを/data/appディレクトリに格納します.携帯電話が再起動すると、PackageManagerServiceの構造方法でこのapkを解析します.PackageManagerServiceのような構造方法がいつ呼び出されるかは、Android frameworkの起動ソースを見てみましょう.ここでは、Androidが起動した後、SystemServerクラスでPackageManagerServiceのmainメソッドが呼び出されます.SystemServer.java
private void startBootstrapServices() {
    ...
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    ...
}

PackageManagerService.java
public static PackageManagerService main(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    ServiceManager.addService("package", m);
    return m;
}

ServiceManagerのaddServiceメソッドを呼び出すと、PERMISSION DENIEDの問題が発生する可能性があります.android add_について説明します.サービスPERMISSION DENIED問題
次の図は、携帯電話の起動後のPackageManagerServiceapp解析のタイミングチャートです.ここで、赤色のマークは、研究プロセスで注目すべき点です.Android动态部署二:APK安装及AndroidManifest.xml解析流程分析_第1张图片
  • PackageParser.JAvaのnew AssetManager&new Resources APKの絶対パスとHost APKのResourcesに基づいて、プラグインAPKのResourcesオブジェクト
  • を初期化できます.
    AssetManager assets = AssetManager.class.newInstance();
    Reflect.create().setClass(AssetManager.class)
            .setMethod("addAssetPath", String.class).invoke(assets, apkPath);
    Resources res = new Resources(assets, context.getResources().getDisplayMetrics(),
                        context.getResources().getConfiguration());

    ここでReflect.JAvaは独自にカプセル化されたJava反射クラスで、簡単に言えば、反射によってAssetManagerのhideメソッドを呼び出す:addAssetPath
  • PackageParser.JAvaのparseBaseApk&parseBaseApplication parseBaseApkメソッドはAndroidManifestを解析します.xmlに含まれるすべての情報、最も重要な部分は呼び出されたparseBaseApplicationメソッドであり、parseBaseApplicationメソッドではAPKに含まれる4つのコンポーネントのすべての情報
  • が解析される.
    private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
            String[] outError) throws XmlPullParserException, IOException {
        AttributeSet attrs = parser;
        ...
    
        final Package pkg = new Package(pkgName);
        TypedArray sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifest);
    
        ...
    
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    
            ...
    
            String tagName = parser.getName();
            if (tagName.equals("application")) {
                ...
    
                if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
                    return null;
                }
            } else if (tagName.equals("overlay")) {
                ...
                XmlUtils.skipCurrentTag(parser);
            } else if (tagName.equals("key-sets")) {
                if (!parseKeySets(pkg, res, parser, attrs, outError)) {
                    return null;
                }
            } else if (tagName.equals("permission-group")) {
                if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
                    return null;
                }
            } else if (tagName.equals("permission")) {
                if (parsePermission(pkg, res, parser, attrs, outError) == null) {
                    return null;
                }
            } else if (tagName.equals("permission-tree")) {
                if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
                    return null;
                }
            } else if (tagName.equals("uses-permission")) {
                if (!parseUsesPermission(pkg, res, parser, attrs)) {
                    return null;
                }
            } else if (tagName.equals("uses-permission-sdk-m")
                    || tagName.equals("uses-permission-sdk-23")) {
                if (!parseUsesPermission(pkg, res, parser, attrs)) {
                    return null;
                }
            } 
            ...
        }
        ...
    
        return pkg;
    }
    
    private boolean parseBaseApplication(Package owner, Resources res,
            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
        throws XmlPullParserException, IOException {
        final ApplicationInfo ai = owner.applicationInfo;
        final String pkgName = owner.applicationInfo.packageName;
    
        TypedArray sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestApplication);
    
        ...    //application    
    
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }
    
            String tagName = parser.getName();
            if (tagName.equals("activity")) {
                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
                        owner.baseHardwareAccelerated);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
    
                owner.activities.add(a);
    
            } else if (tagName.equals("receiver")) {
                Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
    
                owner.receivers.add(a);
    
            } else if (tagName.equals("service")) {
                Service s = parseService(owner, res, parser, attrs, flags, outError);
                if (s == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
    
                owner.services.add(s);
    
            } else if (tagName.equals("provider")) {
                Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
                if (p == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
    
                owner.providers.add(p);
    
            } else if (tagName.equals("activity-alias")) {
                Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }
    
                owner.activities.add(a);
    
            }
            ...
        }
    
        ...
        return true;
    }
  • PackageManagerService.JAvaのscanPackageDirtyLIメソッドでは、1,2で解析した情報をスキャンし、ローカル変数に保存し、queryIntent、resolveIntentなどのメソッドを後で呼び出して、コンポーネントが存在するかどうかを確認し、
  • に戻ります.
    // Currently known shared libraries.
    final ArrayMap<String, SharedLibraryEntry> mSharedLibraries =
            new ArrayMap<String, SharedLibraryEntry>();
    // All available activities, for your resolving pleasure.
    final ActivityIntentResolver mActivities =
            new ActivityIntentResolver();
    // All available receivers, for your resolving pleasure.
    final ActivityIntentResolver mReceivers =
            new ActivityIntentResolver();
    // All available services, for your resolving pleasure.
    final ServiceIntentResolver mServices = new ServiceIntentResolver();
    
    // All available providers, for your resolving pleasure.
    final ProviderIntentResolver mProviders = new ProviderIntentResolver();
    ......
    private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
        final File scanFile = new File(pkg.codePath);
    
        ...
        // writer
        synchronized (mPackages) {
            // Add the new setting to mSettings
            mSettings.insertPackageSettingLPw(pkgSetting, pkg);
            // Add the new setting to mPackages
            mPackages.put(pkg.applicationInfo.packageName, pkg);
            ...
    
            int N = pkg.providers.size();
            StringBuilder r = null;
            int i;
            for (i=0; i...
            }
            ...
    
            N = pkg.services.size();
            r = null;
            for (i=0; i...
            }
            ...
    
            N = pkg.receivers.size();
            r = null;
            for (i=0; i"receiver");
                ...
            }
            ...
    
            N = pkg.activities.size();
            r = null;
            for (i=0; i"activity");
                ...
            }
            ...
    
            N = pkg.permissionGroups.size();
            r = null;
            for (i=0; iif (cur == null) {
                    mPermissionGroups.put(pg.info.name, pg);
                    ...
                }
            }
    
            ...
        }
    
        return pkg;
    }

    adb install xxx&adb shell pm install yyyを使用してapkをインストール
    下図はadb installがAPKをインストールするタイミングチャートで、最終フローはadb pushを使用してインストールするのと同じです:Android动态部署二:APK安装及AndroidManifest.xml解析流程分析_第2张图片
    まとめ
    この文章は主にソースコードのAPKインストールとAndroid Manifestを紹介した.xml解析の一部のソースコードは、Androidの動的配置を行うときに、Android Manifestを自分で解析します.xmlファイルは、このプラグインAPKの情報を取得し、どのようなActivity、Receiver、Service、Providerなどがあるか、自分でコードを書いて解析するのもいいですが、portingソースのPackageParserがお勧めです.JAvaおよびPackageManager.java, PackageManagerService.JAvaの一部のコードは、結局ソースコードがAndroidManifestに対してです.xmlの解析は比較的十分で、漏れがなく、portingの過程でソースコードをより深く理解することができ、私たちの後続の実現のために基礎を築くことができます.