对PKMS的初步探索
想做的事总可以找到时间和机会
不想做的事也总可以找到借口
背景
隐私页的背景是启动页所设置的windowBackground
的图片。
启动页设置windowBackground
为了解决启动时白屏,在AndroidManifest.xml,对隐私页所设置的Theme做了更改,发现怎么设置都是不起作用。在代码中发现,隐私页是非正常流程启动的(startActivity),继承Instrumentation,重写newActivity()方法进行启动Activity;
public class PrivacyInstrumentation extends Instrumentation {
@Override
public Activity newActivity(ClassLoader cl, String className, Intent intent)
throws ClassNotFoundException, IllegalAccessException, InstantiationException {
// Step 1: 首次分发且入口为桌面Launcher点击情况
if (!hasConfirmPrivacy(mApp) && MultiActivity.class.getCanonicalName().equals(className)) {
Intent privacyIntent = new Intent();
String privacyPkgName = PrivacyActivity.class.getPackage().getName();
String privacyClassName = PrivacyActivity.class.getName();
privacyIntent.setClassName(privacyPkgName, privacyClassName);
return mReflect.newActivity(cl, privacyClassName, privacyIntent);
}
// Step 2: 首次分发为外部吊起等情况,或者非首次分发
mReflect.revoke();
rejectHook(mApp);
super.callApplicationOnCreate(mApp);
return mReflect.newActivity(cl, className, intent);
}
}
推测:应该是通过HOOK启动的隐私页,拿到的Activity信息应该是启动页的,xml更改既然失效,那就在代码里去设置,果然显示正常了。
这就结束了吗?不,这才刚刚开始。
- Androidmanifest.xml 是什么时候被解析的?
setTheme()
在setContentView()
之后为什么不生效?
正文开始
Androidmanifest.xml是什么时候被解析的,这就要引入我们今天的主角,PackageManagerService是安卓的核心服务之一,管理Package相关的工作。
1.PKMS的创建时机
PackageManagerService 的创建,是在system_server的run方法通过startBootstrapServices()
调用PKMS的main()
函数然后new PKMS
对象。
private void startBootstrapServices() {
...
//(1)启动Installer
Installer installer = mSystemServiceManager.startService(Installer.class);
mActivityManagerService.setInstaller(installer);
...
//(2)如果设备加密了,则只解析"core"应用
String cryptState = VoldProperties.decrypt().orElse("");
if (ENCRYPTING_STATE.equals(cryptState)) {
Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
mOnlyCore = true;
} else if (ENCRYPTED_STATE.equals(cryptState)) {
Slog.w(TAG, "Device encrypted - only parsing core apps");
mOnlyCore = true;
}
//(3)调用main方法初始化PackageManagerService
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
//PKMS是否是第一次启动
mFirstBoot = mPackageManagerService.isFirstBoot();
...
}
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
//调用PackageManagerService构造方法
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
//往ServiceManager中注册”package”和”package_native”。
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
上面总共做了2件事:
- 在PKMS的main()方法调用PKMS的构造方法
- 将PKMS服务注册到ServiceManager中
在构造方法中分为五个阶段:如下图(借我用用)
从第三个阶段入手:开始扫描我们第三方的APP(data)
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
synchronized (mInstallLock) {
synchronized (mPackages) {
...
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
//这里就是开始扫描我们第三方的APP(data)
scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
...
}
}
}
}
sAppInstallDir = new File(Environment.getDataDirectory(), "app");
Environment.getDataDirectory()
返回的就是’data’目录
private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
public static File getDataDirectory() {
return DIR_ANDROID_DATA;
}
PKMS的过程图如下:
2.PKMS解析第三方APK
那我们从scanDirTracedLI入手:
[ParallelPackageParser.java] scanDirTracedLI()
从下面的函数可见,scanDirTracedLI的入口很简单,首先加入了一些系统的日志追踪,然后调用scanDirLI()进行解析
private void scanDirTracedLI(File scanDir, final int parseFlags, int scanFlags, long currentTime) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
scanDirLI(scanDir, parseFlags, scanFlags, currentTime);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
[ParallelPackageParser.java] scanDirLI()
scanDirLI()中使用了ParallelPackageParser的对象,ParallelPackageParser类中有一个队列,储存的apk
private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = scanDir.listFiles();
//parallelPackageParser是一个队列,收集 apk 文件,
try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
mParallelPackageParserCallback)) {
// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
//是Apk文件,或者是目录
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
过滤掉非 apk 文件,如果不是则跳过继续扫描
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
//把APK信息存入parallelPackageParser中的对象mQueue,PackageParser()函数赋给了队列中的pkg成员
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}
···
}
[ParallelPackageParser.java] submit
public void submit(File scanFile, int parseFlags) {
mService.submit(() -> {
ParseResult pr = new ParseResult();
try {
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
pp.setCacheDir(mCacheDir);
pp.setCallback(mPackageParserCallback);
pr.scanFile = scanFile;
pr.pkg = parsePackage(pp, scanFile, parseFlags);
}
...
try {
mQueue.put(pr);
}
...
}
ParallelPackageParser#submit()
的方法中,new PackageParser
,将Apk交给PackageParser去解析(将文件读取到内存中),最终将APK文件转换成一个package对象,放入到ParallelPackageParser的队列中
[PackageParser.java] parsePackage
通过parsePackage 进行apk解析,如果传入的packageFile是目录,调用parseClusterPackage()解析,
如果传入的是APK文件,就调用parseMonolithicPackage()解析(这里假设传入的就是.apk文件)
public Package parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageParserException {
...
if (packageFile.isDirectory()) {
//如果传入的packageFile是目录,调用parseClusterPackage()解析
parsed = parseClusterPackage(packageFile, flags);
} else {
//如果是APK文件,就调用parseMonolithicPackage()解析
parsed = parseMonolithicPackage(packageFile, flags);
}
...
return parsed;
}
[PackageParser.java] parseMonolithicPackage
parseMonolithicPackage(),它的作用是解析给定的APK文件
最终也是调用parseBaseApk()进行解析,我们接下来看下parseBaseApk()
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
...
final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);
pkg.setCodePath(apkFile.getCanonicalPath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
}
...
}
[PackageParser.java] parseBaseApk
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
...
XmlResourceParser parser = null;
//获得一个 XML 资源解析对象,该对象解析的是 APK 中的 AndroidManifest.xml 文件。
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final Resources res = new Resources(assets, mMetrics, null);
final String[] outError = new String[1];
//再调用重载parseBaseApk()最终到parseBaseApkCommon(),解析AndroidManifest.xml 后得到一个Package对象
final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
...
pkg.setVolumeUuid(volumeUuid);
pkg.setApplicationVolumeUuid(volumeUuid);
pkg.setBaseCodePath(apkPath);
pkg.setSigningDetails(SigningDetails.UNKNOWN);
return pkg;
...
}
parseBaseApk()再调用重载parseBaseApk()最终到parseBaseApkCommon(),
parseBaseApk()主要是对AndroidManifest.xml进行解析,解析后所有的信息放在Package对象中。
[PackageParser.java] parseBaseApkCommon
从AndroidManifest.xml中获取标签名,解析标签中的各个item的内容,存入Package对象中
例如获取标签"application"、“permission”
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
//从AndroidManifest.xml中获取标签名
String tagName = parser.getName();
//如果读到AndroidManifest.xml中的tag是"application",执行parseBaseApplication()进行解析
if (tagName.equals(TAG_APPLICATION)) {
if (foundApp) {
...
}
foundApp = true;
//解析"application"的信息,赋值给pkg
if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
return null;
}
...
//如果标签是"permission"
else if (tagName.equals(TAG_PERMISSION)) {
//进行"permission"的解析
if (!parsePermission(pkg, res, parser, outError)) {
return null;
}
....
}
}
}
}
上面解析AndroidManifest.xml,会得到"application"、"permission"等信息
我们下面就针对"application"进行展开分析一下,进入parseBaseApplication()函数,
获取"application"子标签的标签内容,如果标签是activity
则调用parseActivity方法
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
//获取"application"子标签的标签内容
String tagName = parser.getName();
//如果标签是"activity"
if (tagName.equals("activity")) {
//解析Activity的信息,把activity加入Package对象
Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
hasActivityOrder |= (a.order != 0);
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
//如果标签是"receiver",获取receiver信息,加入Package对象
Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
hasReceiverOrder |= (a.order != 0);
owner.receivers.add(a);
}else if (tagName.equals("service")) {
//如果标签是"service",获取service信息,加入Package对象
Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
hasServiceOrder |= (s.order != 0);
owner.services.add(s);
}else if (tagName.equals("provider")) {
//如果标签是"provider",获取provider信息,加入Package对象
Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
}
...
}
}
parseActivity 中,生成一个Activity类,Activity保存着androidmainfest.xml 里对Activity所设置的属性,theme. launchmode等等;
注意:此Activity同名不同类,成员变量ActivityInfo保存解析出来的信息。
private Activity parseActivity(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError, CachedComponentArgs cachedArgs,
boolean receiver, boolean hardwareAccelerated)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
···
cachedArgs.mActivityArgs.tag = receiver ? "<receiver>" : "<activity>";
cachedArgs.mActivityArgs.sa = sa;
cachedArgs.mActivityArgs.flags = flags;
Activity a = new Activity(cachedArgs.mActivityArgs, new ActivityInfo());
if (outError[0] != null) {
sa.recycle();
return null;
}
boolean setExported = sa.hasValue(R.styleable.AndroidManifestActivity_exported);
if (setExported) {
a.info.exported = sa.getBoolean(R.styleable.AndroidManifestActivity_exported, false);
}
//在AndroidManifest.xml中设置的Theme,就是在这里解析出来的
a.info.theme = sa.getResourceId(R.styleable.AndroidManifestActivity_theme, 0);
a.info.uiOptions = sa.getInt(R.styleable.AndroidManifestActivity_uiOptions,
a.info.applicationInfo.uiOptions);
···
}
在 PackageParser 扫描完一个 APK 后,此时系统已经根据该 APK 中 AndroidManifest.xml,创建了一个完整的 Package 对象,
回顾一下整个APK的扫描过程:
按照system app > other app 优先级扫描APK,解析AndroidManifest.xml文件,得到各个标签内容
解析XML文件得到的信息由 Package 保存。从该类的成员变量可看出,和 Android 四大组件相关的信息分别由 activites、receivers、providers、services 保存。由于一个 APK 可声明多个组件,因此 activites 和 receivers等均声明为 ArrayList。
在 PackageParser 扫描完一个 APK 后,此时系统已经根据该 APK 中 AndroidManifest.xml,创建了一个完整的 Package 对象
ok 到这里就对PKMS解析AndroidManifest.xml的初步探索就结束了。
还有一个问题,为什么在setContentView()
之后设置的setTheme()
不起作用呢?
通过上面,我们知道原来将Androidmanifest.xml 文件中的Activity信息存入了Activity#ActivityInfo中,那我们看看ActivityInfo,是什么时候去用的,这就涉及Activity的启动流程,就不详细去走流程了。这里只看App端(client)的流程,AMS如何获取到ActivityInfo,以及AMS启动Activity的流程就不去追源码了。
ps:这里是7.0以后的代码,7.0以前的流程和这里略微不同
这里就大概回忆一下
ApplicationThread进程:
@Override
public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
ActivityThread.this.scheduleTransaction(transaction);
}
ActivityThread的父类 ClientTransactionHandler
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
发送消息通知ActivityThread处理transaction(省略一系列的函数调用,有兴趣关注 frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java)
最终会根据transaction得到ActivityLifecycleItem,调用execute();
LaunchActivityItem.java
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments);
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
ActivityThread.java
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
...
final Activity a = performLaunchActivity(r, customIntent);
...
return a;
}
我们看到在performLaunchActivity
中,用到了activityInfo
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
// Activity resources must be initialized with the same loaders as the
// application context.
appContext.getResources().addLoaders(
app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
appContext.setOuterContext(activity);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
checkAndBlockForNetworkAccess();
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
//终于找到和主题相关的
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
...
return activity;
}
这样就得到ActivityInfo
,至于ActivityInfo
如何设置到Activity
mInstrumentation.callActivityOnCreate() => Activity#onCreate() => Activity#setContentView() =>
PhoneWindow#setContentView() => PhoneWindow#installDecor() => PhoneWindow#generateLayout()
public final TypedArray obtainStyledAttributes(@NonNull @StyleableRes int[] attrs) {
return getTheme().obtainStyledAttributes(attrs);
}
public final TypedArray getWindowStyle() {
synchronized (this) {
if (mWindowStyle == null) {
mWindowStyle = mContext.obtainStyledAttributes(
com.android.internal.R.styleable.Window);
}
return mWindowStyle;
}
}
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
...
if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {
setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
}
if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,
false)) {
setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS
& (~getForcedWindowFlags()));
}
if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,
false)) {
setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION
& (~getForcedWindowFlags()));
}
...
}
原来是在setContentView中对Theme做了应用,因此我们在setContentView后,setTheme无效。
结束了!!!!
总结
PackageManagerService,是Android系统中核心服务之一,管理着所有跟package相关的工作,常见的比如安装、卸载应用。