zoukankan      html  css  js  c++  java
  • Android系统加载Apk文件的时机和流程分析(1)--Android 4.4.4 r1的源码

                                          本文博客地址:https://blog.csdn.net/QQ1084283172/article/details/80982869

    Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应用程序就是Launcher了。Android系统的Home应用程序Launcher是由ActivityManagerService启动的,而ActivityManagerService和PackageManagerService一样,都是在开机时由SystemServer组件启动的,SystemServer组件首先是启动PackageManagerServic,由它来负责安装系统的应用程序,系统中的应用程序安装好了以后,SystemServer组件接下来就要通过ActivityManagerService来启动Home应用程序Launcher了,Launcher在启动的时候便会通过PackageManagerServic把系统中已经安装好的应用程序以快捷图标的形式展示在桌面上,这样用户就可以使用这些应用程序了。

    上面这段内容,摘自老罗博客《Android系统默认Home应用程序(Launcher)的启动过程源代码分析》,有关点击Android系统的Home界面上的Android应用程序的图标启动Android应用的流程和原理,可以参考老罗的这篇博客。

    Android应用程序的启动是一个非常复杂的流程,涉及到的Android系统的服务和底层的知识也不比较多,这里只学习一下Android系统是在什么时机加载Apk文件的,简单的了解一下Android系统加载Apk文件的流程,后面我会研究一下Xposed多dex的Hook问题。

    在创建Android应用程序的进程时,指定了该进程的入口是ActivityThread的main方法,此时便进入启动Android应用程序的Activity的第4个阶段:加载应用程序的Activity。Android系统加载Apk文件,调试模式等待调试,Android应用程序的类Application实例的创建、attach方法和onCreate方法的被调用都是在这个阶段。

    1.ActivityThread.main

    加载Android应用程序Activity阶段的第一步工作由ActivityThread.main完成,代码如下:

    源码路径:/frameworks/base/core/java/android/app/ActivityThread.java

    在ActivityThread.main方法中,首先设置临时进程名为<pre-initialized>,准备UI主线程的消息循环,然后创建应用程序的主线程ActivityThread,并调用其attach方法,设置传入参数为false,最后进入主线程的消息循环。

    2.ActivityThread.attach

    加载Android应用程序Activity阶段的第二步工作由ActivityThread.attach完成,代码如下:

    中间的无关代码省略......

    在函数ActivityThread.attach中,当传入参数为true时,对应Android系统的system进程的处理流程,此处传入的参数false,对应普通Android应用进程的处理流程。函数ActivityThread.attach首先设置DDMS中显示的临时进程名<pre-initialized>和userId,然后调用ActivityManagerProxy.attachApplication方法,传入该方法的参数mAppThread是在创建ActivityThread实例对象时,初始化为ApplicationThread实例对象的,该实例对象是一个Binder接口,ActivityManagerService便是通过ApplicationThread跨进程调度应用程序进程的。

    3.ActivityManagerService.attachApplication

    加载Android应用程序Activity阶段的第三步工作是由ActivityManagerProxy.attachApplication方法通过Binder通信,进而调用ActivityManagerService的同名方法来完成attach操作的,这里直接分析ActivityManagerService.attachApplication方法,代码如下:

    源码路径:/frameworks/base/services/java/com/android/server/am/ActivityManagerService.java

    在attachApplication中获取到调用者的Pid后,将请求转发给attachApplicationLocked方法进行处理,代码如下:

    省略中间无关的代码......

    省略中间无关的代码......

    ActivityManagerService接收到Android应用程序进程的attach通知后,便会为将要启动的应用程序进程执行以下操作:在mPidsSelfLocked中查找到该Android应用进程的ProcessRecord信息并进一步进行相关参数变量的赋值和设置,在ActivityManagerService消息队列中删除启动应用程序是添加的PROC_START_TIMEOUT_MSG消息,然后紧接着调用类方法thread.bindApplication进入第四步操作,最终调用的是ApplicationThread.bindApplication方法。

    4.ApplicationThread.bindApplication

    加载Android应用程序Activity阶段的第四步工作是ApplicationThread.bindApplication完成的,代码如下:

    源码的路径:/frameworks/base/core/java/android/app/ActivityThread.java

    ApplicationThread.bindApplication将ActivityManagerService传入的数据封装到AppBindData中,然后将AppBindData数据通过BIND_APPLICATION消息发送给Android应用程序主线程的消息循环中,由ActivityThread的 H.handleMessage 处理,代码如下:

    H会根据传入的消息类型,匹配switch代码块的BIND_APPLICATION消息类型分支,获取AppBindData数据,进而调用handleBindApplication方法。

    5.ActivityThread.handleBindApplication

    加载Android应用程序Activity阶段的第五步工作是调用 ActivityThread.handleBindApplication 方法,代码如下:

    省略中间的无关代码......

    省略中间的无关代码......

    省略中间的无关代码......

    ActivityThread.handleBindApplication方法的主要工作如下:

    1).为Android应用程序设置显示的进程名称。

    2).为低内存设备禁用硬件加速。

    3).创建应用程序对应的Application,并设置该Android应用进程的初始Application。

    4).安装ContentProvider(可见ContentProvider的创建先于其他Android组件)。

    5).执行Instrumentation的onCreate方法。

    6).执行Application的onCreate方法。

     // 在apk程序的Activity显示之前执行的代码
        private void handleBindApplication(AppBindData data) {
        	// 获取data传进来的数据并处理保存到相应的描述结构体中
            mBoundApplication = data;
            mConfiguration = new Configuration(data.config);
            mCompatConfiguration = new Configuration(data.config);
    
            mProfiler = new Profiler();
            mProfiler.profileFile = data.initProfileFile;
            mProfiler.profileFd = data.initProfileFd;
            mProfiler.autoStopProfiler = data.initAutoStopProfiler;
    
            // send up app name; do this *before* waiting for debugger
            // 设置Android应用进程的显示名称
            Process.setArgV0(data.processName);
            // 设置Android应用进程在DDMS中显示的名称
            android.ddm.DdmHandleAppName.setAppName(data.processName,
                                                    UserHandle.myUserId());
            // 硬件加速需要消耗较大内存,禁止persistent应用在低内存设备上使用硬件加速
            if (data.persistent) {
                // Persistent processes on low-memory devices do not get to
                // use hardware accelerated drawing, since this can add too much
                // overhead to the process.
                if (!ActivityManager.isHighEndGfx()) {
                    HardwareRenderer.disable(false);
                }
            }
    
            if (mProfiler.profileFd != null) {
                mProfiler.startProfiling();
            }
    
            // If the app is Honeycomb MR1 or earlier, switch its AsyncTask
            // implementation to use the pool executor.  Normally, we use the
            // serialized executor as the default. This has to happen in the
            // main thread so the main looper is set right.
            if (data.appInfo.targetSdkVersion <= android.os.Build.VERSION_CODES.HONEYCOMB_MR1) {
                AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
            }
    
            /*
             * Before spawning a new process, reset the time zone to be the system time zone.
             * This needs to be done because the system time zone could have changed after the
             * the spawning of this process. Without doing this this process would have the incorrect
             * system time zone.
             */
            TimeZone.setDefault(null);
    
            /*
             * Initialize the default locale in this process for the reasons we set the time zone.
             */
            Locale.setDefault(data.config.locale);
    
            /*
             * Update the system configuration since its preloaded and might not
             * reflect configuration changes. The configuration object passed
             * in AppBindData can be safely assumed to be up to date
             */
            mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
            mCurDefaultDisplayDpi = data.config.densityDpi;
            applyCompatConfiguration(mCurDefaultDisplayDpi);
    
            ///////////////////////////////////////////////////////////////////
            // LoadedApk info;	
            // 对Android应用的dex文件进行加载返回LoadedApk实例对象
            data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
            ///////////////////////////////////////////////////////////////////
    
            /**
             * Switch this process to density compatibility mode if needed.
             */
            if ((data.appInfo.flags&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)
                    == 0) {
                mDensityCompatMode = true;
                Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);
            }
            updateDefaultDensity();
    
            // 创建并初始化Android应用程序的Context
            final ContextImpl appContext = new ContextImpl();
            // 使用info的数据对Android应用程序的ContextImpl进行初始化
            appContext.init(data.info, null, this);
            
            if (!Process.isIsolated()) {
            	// cacheDir的路径为/data/data/Android应用包名/cache
                final File cacheDir = appContext.getCacheDir();
                if (cacheDir != null) {
                	// 临时文件目录
                    // Provide a usable directory for temporary files
                    System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
    
                    setupGraphicsSupport(data.info, cacheDir);
                } else {
                    Log.e(TAG, "Unable to setupGraphicsSupport due to missing cache directory");
                }
            }
            /**
             * For system applications on userdebug/eng builds, log stack
             * traces of disk and network access to dropbox for analysis.
             */
            if ((data.appInfo.flags &
                 (ApplicationInfo.FLAG_SYSTEM |
                  ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) {
                StrictMode.conditionallyEnableDebugLogging();
            }
    
            /**
             * For apps targetting SDK Honeycomb or later, we don't allow
             * network usage on the main event loop / UI thread.
             *
             * Note to those grepping:  this is what ultimately throws
             * NetworkOnMainThreadException ...
             */
            if (data.appInfo.targetSdkVersion > 9) {
                StrictMode.enableDeathOnNetwork();
            }
    
            // 判断Android应用是否打开调试模式
            if (data.debugMode != IApplicationThread.DEBUG_OFF) {
                // XXX should have option to change the port.
                Debug.changeDebugPort(8100);
                if (data.debugMode == IApplicationThread.DEBUG_WAIT) {
                    Slog.w(TAG, "Application " + data.info.getPackageName()
                          + " is waiting for the debugger on port 8100...");
    
                    IActivityManager mgr = ActivityManagerNative.getDefault();
                    try {
                        mgr.showWaitingForDebugger(mAppThread, true);
                    } catch (RemoteException ex) {
                    }
                    // 调试模式运行app并调试等待
                    Debug.waitForDebugger();
    
                    try {
                    	// Android程序调试模式启动的提示
                        mgr.showWaitingForDebugger(mAppThread, false);
                    } catch (RemoteException ex) {
                    }
    
                } else {
                    Slog.w(TAG, "Application " + data.info.getPackageName()
                          + " can be debugged on port 8100...");
                }
            }
    
            // Enable OpenGL tracing if required
            if (data.enableOpenGlTrace) {
                GLUtils.setTracingLevel(1);
            }
    
            // Allow application-generated systrace messages if we're debuggable.
            boolean appTracingAllowed = (data.appInfo.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0;
            Trace.setAppTracingAllowed(appTracingAllowed);
    
            /**
             * Initialize the default http proxy in this process for the reasons we set the time zone.
             */
            IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
            if (b != null) {
                // In pre-boot mode (doing initial launch to collect password), not
                // all system is up.  This includes the connectivity service, so don't
                // crash if we can't get it.
                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
                try {
                    ProxyProperties proxyProperties = service.getProxy();
                    Proxy.setHttpProxySystemProperty(proxyProperties);
                } catch (RemoteException e) {}
            }
            // 此处data.instrumentationName为null
            // 对应AndroidManifest.xml的Instrumentation
            if (data.instrumentationName != null) {
                InstrumentationInfo ii = null;
                try {
                    ii = appContext.getPackageManager().
                        getInstrumentationInfo(data.instrumentationName, 0);
                } catch (PackageManager.NameNotFoundException e) {
                }
                if (ii == null) {
                    throw new RuntimeException(
                        "Unable to find instrumentation info for: "
                        + data.instrumentationName);
                }
    
                mInstrumentationAppDir = ii.sourceDir;
                mInstrumentationAppLibraryDir = ii.nativeLibraryDir;
                mInstrumentationAppPackage = ii.packageName;
                mInstrumentedAppDir = data.info.getAppDir();
                mInstrumentedAppLibraryDir = data.info.getLibDir();
    
                ApplicationInfo instrApp = new ApplicationInfo();
                instrApp.packageName = ii.packageName;
                instrApp.sourceDir = ii.sourceDir;
                instrApp.publicSourceDir = ii.publicSourceDir;
                instrApp.dataDir = ii.dataDir;
                instrApp.nativeLibraryDir = ii.nativeLibraryDir;
                
                // 得到Android应用dex文件加载后的LoadedApk实例
                LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                        appContext.getClassLoader(), false, true);
                
                ContextImpl instrContext = new ContextImpl();
                instrContext.init(pi, null, this);
    
                try {
                    java.lang.ClassLoader cl = instrContext.getClassLoader();
                    mInstrumentation = (Instrumentation)
                        cl.loadClass(data.instrumentationName.getClassName()).newInstance();
                } catch (Exception e) {
                    throw new RuntimeException(
                        "Unable to instantiate instrumentation "
                        + data.instrumentationName + ": " + e.toString(), e);
                }
    
                mInstrumentation.init(this, instrContext, appContext,
                       new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
                       data.instrumentationUiAutomationConnection);
    
                if (mProfiler.profileFile != null && !ii.handleProfiling
                        && mProfiler.profileFd == null) {
                    mProfiler.handlingProfiling = true;
                    File file = new File(mProfiler.profileFile);
                    file.getParentFile().mkdirs();
                    Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
                }
    
            } else {
            	// AndroidManifest.xml未指定构建Instrumentation,创建默认值
                mInstrumentation = new Instrumentation();
            }
    
            if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
                dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
            }
    
            // Allow disk access during application and provider setup. This could
            // block processing ordered broadcasts, but later processing would
            // probably end up doing the same disk access.
            final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
            try {
                // If the app is being launched for full backup or restore, bring it up in
                // a restricted environment with the base application class.
            	
            	////////////////////////////////////////////////////////////////
                // 创建Android应用的Application类对象的实例并调用其attach方法
                // 间接通过调用attach方法调用Android应用的attachBaseContext方法
                Application app = data.info.makeApplication(data.restrictedBackupMode, null);
                // 在类ActivityThread的成员变量mInitialApplication中保存创建的Application类对象实例(3)
                // 将第1个Application视为进程的初始化Application
                mInitialApplication = app;
                ////////////////////////////////////////////////////////////////
    
                // don't bring up providers in restricted mode; they may depend on the
                // app's custom Application class
                if (!data.restrictedBackupMode) {
                	// 获取当前Android应用的ContentProvider
                    List<ProviderInfo> providers = data.providers;
                    if (providers != null) {
                        // 安装该Android应用程序的ContentProvider
                        installContentProviders(app, providers);
                        // For process that contains content providers, we want to
                        // ensure that the JIT is enabled "at some point".
                        mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                    }
                }
    
                // Do this after providers, since instrumentation tests generally start their
                // test thread at this point, and we don't want that racing.
                try {
                	// 调用Instrumentationde的OnCreate方法
                    mInstrumentation.onCreate(data.instrumentationArgs);
                } catch (Exception e) {
                    throw new RuntimeException(
                        "Exception thrown in onCreate() of "
                        + data.instrumentationName + ": " + e.toString(), e);
                }
    
                try {
                	// 调用Application实例对象的OnCreate方法
                    mInstrumentation.callApplicationOnCreate(app);
                } catch (Exception e) {
                    if (!mInstrumentation.onException(app, e)) {
                        throw new RuntimeException(
                            "Unable to create application " + app.getClass().getName()
                            + ": " + e.toString(), e);
                    }
                }
            } finally {
                StrictMode.setThreadPolicy(savedPolicy);
            }
        }

    以上步骤执行完毕标志着启动Android应用程序加载Activity的阶段结束,后面的步骤就是进行Activity的显示,暂时不讨论,有必要再研究。我在这里只关注上面标注的3个代码点的位置。上面的代码分析很多参考了《Android的设计与实现 卷I》(作者杨云君)这本书,虽然这本书的作者写的内容对学习Android逆向的同学来说很有帮助,但是感觉作者很多地方分析的不是很详细,要是能说的更明白一点就好了,老罗的博客相对来说就讲的细致和明白一些。

    未完待续,后面再写篇博客详细分析这3个重要代码点的位置,因为这3个位置的代码涉及到Android的加固原理、Android的多dex加载原理以及Xposed Hook的多dex问题的理解~

  • 相关阅读:
    本学期课程总结
    “进度条”博客——第十六周
    “进度条”博客——第十五周
    《梦断代码》阅读笔记03
    第二期冲刺站立会议个人博客16(2016/6/09)
    第二期冲刺站立会议个人博客15(2016/6/08)
    第二期冲刺站立会议个人博客14(2016/6/07)
    第二期冲刺站立会议个人博客13(2016/6/06)
    第二期冲刺站立会议个人博客12(2016/6/05)
    “进度条”博客——第十四周
  • 原文地址:https://www.cnblogs.com/csnd/p/11800587.html
Copyright © 2011-2022 走看看