Android動的配置2:APKインストールおよびAndroid Manifest.xml解析プロセス分析
16630 ワード
転載は出典を明記してください.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をインストールすることができます.
このうち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
PackageManagerService.java
ServiceManagerのaddServiceメソッドを呼び出すと、PERMISSION DENIEDの問題が発生する可能性があります.android add_について説明します.サービスPERMISSION DENIED問題
次の図は、携帯電話の起動後のPackageManagerServiceapp解析のタイミングチャートです.ここで、赤色のマークは、研究プロセスで注目すべき点です. PackageParser.JAvaのnew AssetManager&new Resources APKの絶対パスとHost APKのResourcesに基づいて、プラグインAPKのResourcesオブジェクト を初期化できます.
ここでReflect.JAvaは独自にカプセル化されたJava反射クラスで、簡単に言えば、反射によってAssetManagerのhideメソッドを呼び出す:addAssetPath PackageParser.JAvaのparseBaseApk&parseBaseApplication parseBaseApkメソッドはAndroidManifestを解析します.xmlに含まれるすべての情報、最も重要な部分は呼び出されたparseBaseApplicationメソッドであり、parseBaseApplicationメソッドではAPKに含まれる4つのコンポーネントのすべての情報 が解析される. PackageManagerService.JAvaのscanPackageDirtyLIメソッドでは、1,2で解析した情報をスキャンし、ローカル変数に保存し、queryIntent、resolveIntentなどのメソッドを後で呼び出して、コンポーネントが存在するかどうかを確認し、 に戻ります.
adb install xxx&adb shell pm install yyyを使用してapkをインストール
下図はadb installがAPKをインストールするタイミングチャートで、最終フローはadb pushを使用してインストールするのと同じです:
まとめ
この文章は主にソースコードのAPKインストールとAndroid Manifestを紹介した.xml解析の一部のソースコードは、Androidの動的配置を行うときに、Android Manifestを自分で解析します.xmlファイルは、このプラグインAPKの情報を取得し、どのようなActivity、Receiver、Service、Providerなどがあるか、自分でコードを書いて解析するのもいいですが、portingソースのPackageParserがお勧めです.JAvaおよびPackageManager.java, PackageManagerService.JAvaの一部のコードは、結局ソースコードがAndroidManifestに対してです.xmlの解析は比較的十分で、漏れがなく、portingの過程でソースコードをより深く理解することができ、私たちの後続の実現のために基礎を築くことができます.
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解析のタイミングチャートです.ここで、赤色のマークは、研究プロセスで注目すべき点です.
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
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;
}
// 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を使用してインストールするのと同じです:
まとめ
この文章は主にソースコードのAPKインストールとAndroid Manifestを紹介した.xml解析の一部のソースコードは、Androidの動的配置を行うときに、Android Manifestを自分で解析します.xmlファイルは、このプラグインAPKの情報を取得し、どのようなActivity、Receiver、Service、Providerなどがあるか、自分でコードを書いて解析するのもいいですが、portingソースのPackageParserがお勧めです.JAvaおよびPackageManager.java, PackageManagerService.JAvaの一部のコードは、結局ソースコードがAndroidManifestに対してです.xmlの解析は比較的十分で、漏れがなく、portingの過程でソースコードをより深く理解することができ、私たちの後続の実現のために基礎を築くことができます.