Loading...
墨滴

androidGreenHand

2021/08/19  阅读:130  主题:默认主题

对PKMS的初步探索

对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更改既然失效,那就在代码里去设置,果然显示正常了。

正常的隐私页样式
正常的隐私页样式

这就结束了吗?不,这才刚刚开始。

  1. Androidmanifest.xml 是什么时候被解析的?
  2. 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件事:

  1. 在PKMS的main()方法调用PKMS的构造方法
  2. 将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,
                    truefalse);
            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相关的工作,常见的比如安装、卸载应用。

参考

PackageManagerService源码分析

androidGreenHand

2021/08/19  阅读:130  主题:默认主题

作者介绍

androidGreenHand