SharedLibrary and Dynamic Reference ------- Framework processing in Android resource management

We have compiled the resource sharing library lib-out.apk (package name: com.google.android.test.shared_library) and referenced the application App-out.apk of this shared library (package name: com.google.android.test.lib_client). The rest is to install and run it. Needless to say, but how does our App load our resource sharing library when running? Do you remember our two questions in the last issue and the expected scene of the car accident?

Briefly describe the application startup process
We know that when the init process is up, it will parse the init. * * * * *. RC file, perform many operations, such as mounting partitions, permission control, and start many services and processes, such as service_manager,media_server, Zygote, etc. The Zygote process is system_server and the parent process of all our Android application processes. Its code is in frameworks / base / CMDS / APP_ Under process, we won't do too much introduction here. There will be an article to discuss it later. After the Zygote process is up, it will create a virtual machine, then load ZygoteInit.java, execute its main function, and then enter the Java world, fork out the systemServer, and then it will create a socket as a server to wait and process requests from other application processes.

When our App process is fork ed out, it will call the main method of ActivityTread. In the main method of ActivityThread, it will pass its iaapplicationthread interface to systemServer (or AMS) through the binder call AttachApplication. In this way, a two-way communication mechanism between our Application process and AMS is established (cross process, one is the Application process and the other is the systemServer process) When AMS handles the AttachApplication request of our Application process, it will call back the bindApplication method of the Application process through iaapplicationthread again, and will bring a lot of parameters. Our Application process will set its own process name, create the Application, and execute its onCreate method according to these parameters, which will enter the familiar process .

Start with a parameter of bindApplication
There are many parameters passed from the bindApplication method of AMS:

        public final void bindApplication(String processName, ApplicationInfo appInfo,
                ProviderInfoList providerList, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableBinderTracking, boolean trackAllocation,
                boolean isRestrictedBackupMode, boolean persistent, Configuration config,
                CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
                String buildSerial, AutofillOptions autofillOptions,
                ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) {

Now we only focus on the second one, ApplicationInfo. I wonder if you still have an impression of ApplicationInfo introduced in SharedLibrary and Dynamic Reference - resource sharing library in Android resource management (I). Let's recall:

    /**
     * Full path to the base APK for this application.
     */
    public String sourceDir;
    /**
     * Full paths to the locations of extra resource packages (runtime overlays)
     * this application uses. This field is only used if there are extra resource
     * packages, otherwise it is null.
     *
     * {@hide}
     */
    @UnsupportedAppUsage
    public String[] resourceDirs;
        /**
     * Paths to all shared libraries this application is linked against.  This
     * field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
     * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving
     * the structure.
     */
    public String[] sharedLibraryFiles;
    /**
     * Full path to the directory where native JNI libraries are stored.
     */
    public String nativeLibraryDir;

Yes, the shared library files variable of applicationinfo will be the path of our resource sharing library package com.google.android.test.shared_library.

Where does the value of applicationinfo.sharedlibraryfiles come from

Since this sharedlibrayfiles is the path of the resource library referenced by our App, how does the system get it? In fact, this is very simple. PMS is responsible for parsing and saving package related information, and this is certainly no exception. We use the uses library tag in our App's AndroidManifest.xml, so PMS can definitely resolve it. We Simply follow:

private final boolean attachApplicationLocked(int pid) {
          .........

          ApplicationInfo appInfo = app.instrumentationInfo != null
                    ? app.instrumentationInfo : app.info;

          .........

          thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());

}

The app here is an instance of ProcessRecord, and ProcessRecord will pass in an ApplicationInfo instance when it is constructed. This ApplicationInfo instance comes from:

Implementation of ActivityThread.java:

Get the proxy directly from the ServiceManager, and the corresponding implementation class is PackageManagerService, that is
PMS, follow in:

This is relatively simple. Enter PackageParser.java. As the name suggests, this class is used to parse package information:

    public static ApplicationInfo generateApplicationInfo(Package p, int flags,
            PackageUserState state, int userId) {
        if (p == null) return null;
        if (!checkUseInstalledOrHidden(flags, state)) {
            return null;
        }
        if (!copyNeeded(flags, p, state, null, userId)
                && ((flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) == 0
                        || state.enabled != PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
            // In this case it is safe to directly modify the internal ApplicationInfo state:
            // - CompatibilityMode is global state, so will be the same for every call.
            // - We only come in to here if the app should reported as installed; this is the
            // default state, and we will do a copy otherwise.
            // - The enable state will always be reported the same for the application across
            // calls; the only exception is for the UNTIL_USED mode, and in that case we will
            // be doing a copy.
            updateApplicationInfo(p.applicationInfo, flags, state);
            return p.applicationInfo;
        }

        // Make shallow copy so we can store the metadata/libraries safely
        ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
        if (userId != 0) {
            ai.uid = UserHandle.getUid(userId, ai.uid);
            ai.dataDir = PackageManager.getDataDirForUser(userId, ai.packageName);
        }
        if ((flags & PackageManager.GET_META_DATA) != 0) {
            ai.metaData = p.mAppMetaData;
        }
        if ((flags & PackageManager.GET_SHARED_LIBRARY_FILES) != 0) {
            ai.sharedLibraryFiles = p.usesLibraryFiles;
        }
        if (state.stopped) {
            ai.flags |= ApplicationInfo.FLAG_STOPPED;
        } else {
            ai.flags &= ~ApplicationInfo.FLAG_STOPPED;
        }
        updateApplicationInfo(ai, flags, state);
        return ai;
    }

Finally, we can see that sharedlibrayfiles comes from the package information in PackageParser. How does PackageParser get it? Of course, it is obtained by parsing AndroidManifest.xml. Let's look directly at its parseBaseApk method this time:

private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
            String[] outError) throws XmlPullParserException, IOException {
         //Resolve package name

         //Resolve version name

         //Parsing version code

         //Parsing sharedUId information

 

          //Parse each element

          while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                 .........

                  //Finally resolved to uses library. It's so kind

            } else if (tagName.equals("uses-library")) {
                sa = res.obtainAttributes(attrs,
                        com.android.internal.R.styleable.AndroidManifestUsesLibrary);

                // Note: don't allow this value to be a reference to a resource
                // that may change.
                String lname = sa.getNonResourceString(
                        com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
                boolean req = sa.getBoolean(
                        com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
                        true);

                sa.recycle();

                if (lname != null) {
                    lname = lname.intern();
                    if (req) {
                        //This is a library that must be relied on. Without it, we will fail when installing

                        //Corresponding to android:required="true"
                        owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
                    } else {
                        //This is an optional dependency library. It is not checked during installation, but an error will occur if it is not found at runtime

                        //Corresponding to android:required="false"
                        owner.usesOptionalLibraries = ArrayUtils.add(
                                owner.usesOptionalLibraries, lname);
                    }
                }

                XmlUtils.skipCurrentTag(parser);

            }

                 .........

           }

}

Finally, the parsing is complete, but not yet complete. We said that ApplicationInfo.sharedLibraryFiles is the path of the resource library, but at this time, we see that the following is stored in PackageParser.Package:
The value of this attribute: that is, the name of the library we use, which is not right!

In fact, PMS gives us a little magic:

In the sharedlibrarientry class, path represents the path of our shared library, and apk is the Package name of the shared library. mSharedLibraries is a Map, and its key is the name of the shared library. Let's look at the updateSharedLibrariesLPw and addsharedlibrarieslpw methods: it will traverse all libraries that our App must and optionally depend on, and take out their names (note that it is the name of the library, not the Package name), then get the corresponding sharedlibrayentry according to the name of the library, find the corresponding Package object from the sharedlibrayentry according to the Package name, take out the real path, and put the path into a temporary array useslibrayfiles. Finally, replace the corresponding array in the Package object with this temporary array!

However, there is a problem now. Where did the elements in the Map mSharedLibraries come from? They must have been added from shared library packages one by one when PMS scanned packages. In fact, it comes from two sources. One is during PMS Construction:

public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
            ..........

            ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
            for (int i=0; i<libConfig.size(); i++) {
                mSharedLibraries.put(libConfig.keyAt(i),
                        new SharedLibraryEntry(libConfig.valueAt(i), null));
            }

             ..........

}

We can see that it will read from SystemConfig, but the data in SystemConfig seems to only have the name and path of the library. The package name is null: new sharedlibrayentry (libconfig. Valueat (I), null). Let's see how SystemConfig is implemented:

//frameworks/base/services/core/java/com/android/server/SystemConfig.java

SystemConfig() {
        // Read configuration from system
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "sysconfig"), false);
        // Read configuration from the old permissions dir
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "permissions"), false);
        // Only read features from OEM config
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "sysconfig"), true);
        readPermissions(Environment.buildPath(
                Environment.getOemDirectory(), "etc", "permissions"), true);
    }

During construction, read the configuration file from the permissions and sysconfig directories of / etc and / oem/etc directories:

void readPermissions(File libraryDir, boolean onlyFeatures) {
      .........

       for (File f : libraryDir.listFiles()) {
            // We'll read platform.xml last
            if (f.getPath().endsWith("etc/permissions/platform.xml")) {
                platformFile = f;
                continue;
            }

            if (!f.getPath().endsWith(".xml")) {
                Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
                continue;
            }
            if (!f.canRead()) {
                Slog.w(TAG, "Permissions library file " + f + " cannot be read");
                continue;
            }

            readPermissionsFromXml(f, onlyFeatures);
        }

        if (platformFile != null) {
            readPermissionsFromXml(platformFile, onlyFeatures);
        }

}

Resolution:

private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
        ...........

        XmlPullParser parser = Xml.newPullParser();

        ..........

        

         else if ("library".equals(name) && !onlyFeatures) {
                    String lname = parser.getAttributeValue(null, "name");
                    String lfile = parser.getAttributeValue(null, "file");
                    if (lname == null) {
                        Slog.w(TAG, "<library> without name in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else if (lfile == null) {
                        Slog.w(TAG, "<library> without file in " + permFile + " at "
                                + parser.getPositionDescription());
                    } else {
                        //Log.i(TAG, "Got library " + lname + " in " + lfile);
                        mSharedLibraries.put(lname, lfile);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                }

}

This part of the code is very simple. Parse the tag and write the results to mSharedLibraries. We won't explain more. You can take a look at the content in / etc/permissions/platform.xml to have an intuitive understanding:

Sure enough, there is only the name and path of the library. We can see from here that we can add a resource sharing library through such a configuration file, but this requires system permission. This method is suitable for various mobile phone manufacturers.
Another source of mssharedlibraries in PMS is that we won't introduce it in detail when scanning each package. Interested students can check it in the PMS source code.

What does the system do with applicationinfo.sharedlibraryfiles

Now that we have fully known the origin of applicationinfo.sharedlibraryfiles, what about the pulse? What does the Framework do with it? We continue to return to the bindApplication method when the App is started. Of course, this is a binder method, which will call our App process from AMS. As mentioned earlier, the Application process will set its own process name, create Base Context, create Application, and execute its onCreate method. Here we focus on the creation of Base Context:

//frameworks/base/core/java/android/app/ContextImpl.java

static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
        return new ContextImpl(null, mainThread,
                packageInfo, null, null, false, null, null);
}

When creating a Context, two very important parameters will be passed in: mainThread and packageInfo. In fact, Context is often used and called Context, but it is a very vague concept. Different people may have different understanding of Context. I think it can be understood from three aspects:

  1. It has an instance of ActivityThread inside. The ActivityThread and its internal Binder interface iaapplicationthread are the bridge of two-way interaction between our Application process and SystemServer, that is, Context will interact with SystemServer on behalf of our Application process and accept the scheduling of SystemServer. For example, the life cycle management of our Application and the four components are inseparable from this bridge of interactive scheduling.
  2. It has an instance of LoadedApk inside. LoadedApk has other functions, but in Context, its function is mainly reflected in the package information carrier. For example, our application package name, version, library used, etc.
  3. It has our resources inside. It is used to load and reference resources.

Here, let's briefly talk about the encapsulation of Resources: ContextImpl - > Resources - > AssetManager. AssetManager is a member of Resources, which is a member of ContextImpl. When constructing ContextImpl, it will load Resources and go to the getResources method of LoadedApk:

public Resources getResources(ActivityThread mainThread) {
        if (mResources == null) {
            mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,
                    mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);
        }
        return mResources;

 }

Let's take a look at who the miapplicationinfo.sharedlibraryfiles are finally passed to:

//ActivityThread.java

/**
     * Creates the top level resources for the given package.
     */
    Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
            String[] libDirs, int displayId, Configuration overrideConfiguration,
            LoadedApk pkgInfo) {
        return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
                displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
    }

Passed to ResourcesManager:

public Resources getTopLevelResources(String resDir, String[] splitResDirs,
            String[] overlayDirs, String[] libDirs, int displayId,
            Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
             Resources r;

            //First check the cache. If it has been loaded, it will be returned directly

           ..............

            //Create AssetManager, a real asset management class object

            AssetManager assets = new AssetManager();

            //Our application itself is also a resource package, and its path should be added to AssetManager

            if (resDir != null) {
                   if (assets.addAssetPath(resDir) == 0) {
                          return null;
                   }
            }

           ...............

            //Add all referenced resource sharing library paths

            if (libDirs != null) {
                   for (String libDir : libDirs) {
                         if (assets.addAssetPath(libDir) == 0) {
                                Slog.w(TAG, "Asset path '" + libDir +
                                      "' does not exist or contains no resources.");
                          }
                     }
             }

            .................

            //Create Resources class object

            r = new Resources(assets, dm, config, compatInfo, token);

            return r;

}

So far, we are very familiar with the familiar Resources class. Our has added the resource library to its AssetManager, but can we get the string resource in the resource library through Resources.getString()?

Original link: https://blog.csdn.net/dayong198866/article/details/95457830

Keywords: Java Android

Added by kernelgpf on Fri, 22 Oct 2021 02:09:25 +0300