• ContentProvider启动流程分析(三)


    0x01 扯东扯西的前言&概述


    本片博客对应时序图上的step15—26上接第二篇博客:ContentProvider启动流程分析二!

    同时此系列博客同步在简书发布:ContentProvider启动流程分析系列!详情戳这里即可访问~

    0x02 ContentProvider启动流程分析


    step15: ApplicationThread#bindApplication()

    上一步,在ApplicationThreadProxy类的bindApplication()函数中,通过Binder对象mRemote发出了一个进程间通信请求!

    反查源码很容易知道,bindApplication()函数,其实是IApplicationThread接口类的接口函数,而ActivityThread类的内部类ApplicationThread实现了这一接口,所以自然也就实现了接口函数bindApplication(),我们来看ApplicationThread类的函数bindApplication()源码删减如下:

    public final void bindApplication(String processName, ApplicationInfo appInfo,
                List<ProviderInfo> providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
                Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
                Bundle coreSettings) {
    	AppBindData data = new AppBindData();
    	data.processName = processName;
    	data.appInfo = appInfo;
    	data.providers = providers;
    	data.instrumentationName = instrumentationName;
    	data.instrumentationArgs = instrumentationArgs;
    	data.instrumentationWatcher = instrumentationWatcher;
    	data.instrumentationUiAutomationConnection = instrumentationUiConnection;
    	data.debugMode = debugMode;
    	data.enableOpenGlTrace = enableOpenGlTrace;
    	data.restrictedBackupMode = isRestrictedBackupMode;
    	data.persistent = persistent;
    	data.config = config;
    	data.compatInfo = compatInfo;
    	data.initProfilerInfo = profilerInfo;
    	sendMessage(H.BIND_APPLICATION, data);
    }
    
    • 没错!ApplicationThread类的成员函数bindApplication(),就是类型为BIND_APPLICATION_TRANSACTION的进程间通信请求的真正处理者!处理的逻辑是:先把将要启动的ContentProvider组件列表,封装成一个AppBindData对象;
    • 然后再调用外部类ActivityThread的成员函数sendMessage(),再次将这个AppBindData对象封装成一个类型为BIND_APPLICATION的消息,以便可以发送到新建应用程序进程的主线程的消息队列中!

    step16: ActivityThread#sendMessage()

    ActivityThread类持有一个mH成员变量,mH实际上是一个Handler对象,通过mH.sendMessage()发送消息到新建应用程序进程的主线程的消息队列中!所以,这个消息最后是最终是在mH.handleMessage()函数中处理的。

    step17: ActivityThread#handleMessage()

    ActivityThread内部类H的程艳方法handleMessage()源码删减如下:

    public void handleMessage(Message msg) {
    	if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    	switch (msg.what) {
    		......
    	case BIND_APPLICATION:
    		AppBindData data = (AppBindData)msg.obj;
    		handleBindApplication(data);
    		break;
    		......
    	}
    	if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
    }
    
    • 在handleMessage()函数中,我们只考虑BIND_APPLICATION的情况,首先将Message对象msg的成员变量obj强转成个AppBindData对象,它包含了要启动的ContentProvider列表。
    • 然后,调用handleBindApplication()函数把这些ContentProvider组件启动起来!

    接下来,关注handleBindApplication()函数,是怎样把这些ContentProvider组件启动起来的?

    step18: ActivityThread#handleBindApplication()

    ActivityThread类的成员函数handleBindApplication()源码如下:

    private void handleBindApplication(AppBindData data) {
    	......
    	// don't bring up providers in restricted mode; they may depend on the
    	// app's custom Application class
    	if (!data.restrictedBackupMode) {
    		List<ProviderInfo> providers = data.providers;
    		if (providers != null) {
    			installContentProviders(app, providers);
    			......
    		}
    	}
    }
    

    参数data是一个AppBindData对象,data的成员变量providers保存了,需要在当前进程中启动的ContentProvider组件,接下来则会调用ActivityThread类的成员函数installContentProviders()来启动这些组件!

    step19: ActivityThread#installContentProviders()

    ActivityThread类的成员函数installContentProviders()源码如下:

    private void installContentProviders(
        Context context, List<ProviderInfo> providers) {
    	final ArrayList<IActivityManager.ContentProviderHolder> results =
    	    new ArrayList<IActivityManager.ContentProviderHolder>();
    
    	for (ProviderInfo cpi : providers) {
    		IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
    		if (cph != null) {
    			cph.noReleaseNeeded = true;
    			results.add(cph);
    		}
    	}
    
    	try {
    		ActivityManagerNative.getDefault().publishContentProviders(
    		    getApplicationThread(), results);
    	} catch (RemoteException ex) {
    	}
    }
    
    • for-each循环得到保存在providers中的每一个组件ProviderInfo对象cpi,然后调用installProvider()函数启动每个cpi对应的ContnetProvider组件;
    • 这些组件启动之后,就可以获得其对应的一个IContentProvider接口;然后,把这个接口封装成一个ContentProviderHolder对象;
    • 最后调用AMS代理对象(也即,ActivityManagerProxy)的成员函数publishContentProviders(),将这些ContentProviderHolder对象传递给AMS;AMS正是通过这些ContentProviderHolder对象获取它所描述的ContentProvider组件的一个访问接口;

    接下来,先分析ActivityThread类成员函数installProvider()在当前应用程序进程中启动一个ContentProvider组件的过程;然后,分析AMS代理对象ActivityManagerProxy成员函数publishContentProviders()将启动完成的组件发布到AMS的过程!

    step20: ActivityThread#installProvider()

    ActivityThread类成员函数installProvider()源码如下:

    /**
     * Installs the provider.
     *
     * Providers that are local to the process or that come from the system server
     * may be installed permanently which is indicated by setting noReleaseNeeded to true.
     * Other remote providers are reference counted.  The initial reference count
     * for all reference counted providers is one.  Providers that are not reference
     * counted do not have a reference count (at all).
     *
     * This method detects when a provider has already been installed.  When this happens,
     * it increments the reference count of the existing provider (if appropriate)
     * and returns the existing provider.  This can happen due to concurrent
     * attempts to acquire the same provider.
     */
    private IActivityManager.ContentProviderHolder installProvider(Context context,
            IActivityManager.ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
    	ContentProvider localProvider = null;
    	IContentProvider provider;
    	// holder为空,表示参数info所描述的ContentProvider组件需要在当前进程中启动
    	if (holder == null || holder.provider == null) {
    		Context c = null;
    		ApplicationInfo ai = info.applicationInfo;
    		if (context.getPackageName().equals(ai.packageName)) {
    			c = context;
    		}
    		......
    		final java.lang.ClassLoader cl = c.getClassLoader();
    		localProvider = (ContentProvider)cl.
    		                loadClass(info.name).newInstance();
    		provider = localProvider.getIContentProvider();
    		if (provider == null) {
    			return null;
    		}
    		// XXX Need to create the correct context for this provider.
    		localProvider.attachInfo(c, info);
    	} else {
    		provider = holder.provider;
    	}
    
    	IActivityManager.ContentProviderHolder retHolder;
    	synchronized (mProviderMap) {
    		IBinder jBinder = provider.asBinder();
    		if (localProvider != null) {
    			ComponentName cname = new ComponentName(info.packageName, info.name);
    			ProviderClientRecord pr = mLocalProvidersByName.get(cname);
    			if (pr != null) {
    				provider = pr.mProvider;
    			} else {
    				holder = new IActivityManager.ContentProviderHolder(info);
    				holder.provider = provider;
    				holder.noReleaseNeeded = true;
    				pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
    				mLocalProviders.put(jBinder, pr);
    				mLocalProvidersByName.put(cname, pr);
    			}
    			retHolder = pr.mHolder;
    		} else {
    			ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
    			if (prc != null) {
    				......
    			} else {
    				ProviderClientRecord client = installProviderAuthoritiesLocked(
    				                                  provider, localProvider, holder);
    				if (noReleaseNeeded) {
    					prc = new ProviderRefCount(holder, client, 1000, 1000);
    				} else {
    					prc = stable ? new ProviderRefCount(holder, client, 1, 0)
    					      : new ProviderRefCount(holder, client, 0, 1);
    				}
    				mProviderRefCountMap.put(jBinder, prc);
    			}
    			retHolder = prc.holder;
    		}
    	}
    	return retHolder;
    }
    
    • 对传进来的参数holder判空,如果为null,表示要在当前应用程序进程中,将参数info所描述的ContentProvider组件启动起来;另一个参数context描述了将要启动的ContentProvider组件运行的上下文环境;
    • localProvider表示一个ContentProvider组件对象,通过localProvider.getIContentProvider(),也即调用localProvider的成员函数getIContentProvider()来获得该组件对应的一个IContentProvider接口provider;这个IContentProvider最终需要发布到AMS,AMS会将它返回给那些需要访问它所描述的ContentProvider组件的调用方应用程序进程;
    • 接下来通过localProvider.attachInfo()来进一步初始化前面所创建的ContentProvider组件;
    • 接下来对全局变量mProviderMap加同步锁,然后对localProvider判空,正常情况下此时的localProvider应该是非空的,所以,自然要把它保存到mLocalProviders和mLocalProvidersByName中;
    • 但是如果localProvider仍然为空,接下来会出现一个ProviderRefCount对象prc,用来表示和记录ContentProvider组件的引用计数,如果prc为空,则把这个localProvider封装成一个ProviderClientRecord对象,并保存在prc变量中;
    • 最后,返回这个ContentProviderHolder对象!

    接下来,继续分析ContentProvider组件的成员函数getIContentProvider()和成员函数attachInfo()的实现细节!

    step21: ContentProvider#getIContnetProvider()

    ContentProvider类成员函数getIContentProvider()源码如下:

    public abstract class ContentProvider implements ComponentCallbacks2 {
    	......
    	private Transport mTransport = new Transport();
    	......
        class Transport extends ContentProviderNative {......}
    	......
    	public IContentProvider getIContentProvider() {
            return mTransport;
        }
    	......
    }
    
    • 每一个ContentProvider组件都有一个内部类Transport,其本质是一个Binder本地对象;并且持有一个类型为Transport的全局变量mTransPort来表示一个Binder本地对象。
    • 通过将这个Binder本地对象传递给AMS,然后AMS会将引用了这个Binder本地对象的一个Binder代理对象返回给需要访问该ContentProvider组件的其他应用程序进程;这样,其他应用程序进程就可以通过这个Binder代理对象来间接的访问一个ContentProvider组件中的数据了!
    • ContentProvider类成员函数getIContentProvider()的实现很简单,只是简单地将全局变量mTransPort描述的一个IContentProvider接口返回给调用者。

    step22: ContentProvider#attachInfo()

    ContentProvider类的成员函数attachInfo(),作用是初始化前面所创建的一个ContentProvider组件!attachInfo()源码如下:

    public abstract class ContentProvider implements ComponentCallbacks2 {
    	......
        /**
         * After being instantiated, this is called to tell the content provider
         * about itself.
         *
         * @param context The context this provider is running in
         * @param info Registered information about this content provider
         */
        public void attachInfo(Context context, ProviderInfo info) {
            attachInfo(context, info, false);
        }
    
        private void attachInfo(Context context, ProviderInfo info, boolean testing) {
    		......
            /*
             * Only allow it to be set once, so after the content service gives
             * this to us clients can't change it.
             */
            if (mContext == null) {
                mContext = context;
                if (context != null) {
                    mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
                            Context.APP_OPS_SERVICE);
                }
                mMyUid = Process.myUid();
                if (info != null) {
                    setReadPermission(info.readPermission);
                    setWritePermission(info.writePermission);
                    setPathPermissions(info.pathPermissions);
    				......
                }
                ContentProvider.this.onCreate();
            }
        }
    	......
    }
    
    • 依然是两个重载函数,接收两个参数的attachInfo()函数内部调用了接收三个参数的attachInfo()函数,我们直接关注接收三个参数的attachInfo()函数;
    • setReadPermission(info.readPermission),setWritePermission(info.writePermission)和setPathPermissions(info.pathPermissions)这三个函数的作用是,设置ContentProvider组件的读写权限和访问权限;
    • 最后ContentProvider.this.onCreate()函数,实际调用的是COntentProvider子类的onCreate()函数,以便在子类的onCreate()函数中,执行一些业务相关的初始化操作!
    • 在我们最开始的场景中,ContentProvider组件指的就是BxxApp应用程序进程中SubContentProvider组件,所以上面调用的时加上就是SubContentProvider类的onCreate()函数,而我们的业务逻辑相关的一些初始化工作,也正是放在SubContentProvider类的onCreate()函数中执行的!

    step23: SubContentProvider#onCreate()

    public class SubContentProvider extends ContentProvider {
    	......
    	@Override
    	public boolean onCreate() {
    		ContentResolver resolver = getContext().getContentResolver();
    		DatabaseHelper helper = new DtatabaseHelper(......);
    		......
    		return true;
    	}
    	......
    }
    
    • 需要注意,由于一个ContentProvider组件再启动过程中需要执行onCreate()函数,因此,我们应该避免在onCeate()方法中执行耗时操作,例如和IO相关的操作,否则可能造成这个ContentProvider组件启动超时!

    这一步执行完成后,就会返回到前面的step20,然后再次返回到前面的step19,即ActivityThread类成员函数installContentProviders()中,接下来就会调用AMS代理对象的成员函数publishContentProviders(),也即ActivityManagerProxy的成员函数publishContentProviders(),将前面所有启动的ContentProvider组件的一个IContentProvider访问接口发布到ActivityManagerService中~

    step24: ActivityManagerProxy#publishContentProviders()

    ActivityManagerProxy类成员函数publishContentProviders(),会将所有启动的ContentProvider组件的一个IContentProvider访问接口发布到ActivityManagerService中,源码如下:

    public void publishContentProviders(IApplicationThread caller,
            List<ContentProviderHolder> providers) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeTypedList(providers);
        mRemote.transact(PUBLISH_CONTENT_PROVIDERS_TRANSACTION, data, reply, 0);
        reply.readException();
        data.recycle();
        reply.recycle();
    }
    
    • 先把接收的参数保存到Parcel对象data中;
    • 接着再通过ActivityManagerProxy类内部的一个Binder代理对象mRemote向ActivityMnagerService发送一个类型为PUBLISH_CONTENT_PROVIDERS_TRANSACTION进程间通信请求;

    step25: ActivityManagerService#publishContentProviders()

    以上10步都是在新建的应用程序进程中执行的!step25是在ActivityManagerService中执行的,AMS类成员函数publishContentProviders(),用来处理类型为PUBLISH_CONTENT_PROVIDERS_TRANSACTION进程间通信请求,其源码如下:

    AMS#publishContentProviders()

    • 参数caller是一个类型为ApplicationThread的Binder代理对象,它引用了运行在新建应用程序进程中的一个ApplicationThread对象,getRecordForAppLocked(caller)方法通过caller来获取一个用来描述新建应用程序进程的ProcessRecord对象r;
    • 新建应用程序进程在启动时,会将需要在它里面运行的ContentProvider组件启动起来!从前面的step13可知,在AMS中,这些ContentProvider组件使用一个ContentProviderRecord对象来描述,它们保存在用来描述新建应用程序进程的一个ProcessRecord对象r的一个成员变量pubProviders中;
    • 第10到14行,第一个for循环,取出providers中的每一个ContentProvider组件,并且拿到ContentProvider组件对应的ContentProviderRecord对象dst;
    • 第15到22行,第二个for循环,通过两种方式,把ContentProviderRecord对象dst保存到全局变量mProviderMap;
    • 参数providers包含了要发布到AMS中的ContentProvider组件,每一个ContentProvider组件都使用一个ContentProviderHolder对象来描述,它里面包含了要发布的ContentProvider组件的一个IContentProvider接口,如图第34行到40行所示!

    从前面的step8可知,一个应用程序进程请求ActivityManagerService返回一个ContentProvider组件的代理对象时,如果这个ContentProvider组件还未启动起来,那么AMS就会先创建一个新的应用程序进程来启动该ContentProvider组件,然后再在一个while循环中等待该ContentProvider组件启动完成,并且将他的一个代理对象发布到AMS中。

    现在既然这个ContentProvider已经启动完成,并且将它的一个代理对象,即一个类型为Transport的Binder代理对象发布到AMS,因此,前面正在等待的一个AMS线程就可以停止等待,并且将这个类型为Transport的Binder代理对象封装成一个ContentProvider对象返回给请求它的应用程序进程。

    这一步执行完毕,就会使得AMS从前面的step8返回step5,即返回到MainActivity组件所运行的应用程序进程中,即AxxApp应用程序进程!然后,继续执行ActivityThread类成员函数installProvider(),用来保存前面从ActivityManagerService获得的一个ContentProvider组件的一个IContentProvider访问接口。

    step26: ActivityThread#installProvider()

    ActivityThread类成员函数installProvider()源码如下:

    /**
     * Installs the provider.
     *
     * Providers that are local to the process or that come from the system server
     * may be installed permanently which is indicated by setting noReleaseNeeded to true.
     * Other remote providers are reference counted.  The initial reference count
     * for all reference counted providers is one.  Providers that are not reference
     * counted do not have a reference count (at all).
     *
     * This method detects when a provider has already been installed.  When this happens,
     * it increments the reference count of the existing provider (if appropriate)
     * and returns the existing provider.  This can happen due to concurrent
     * attempts to acquire the same provider.
     */
    private IActivityManager.ContentProviderHolder installProvider(Context context,
            IActivityManager.ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
    	ContentProvider localProvider = null;
    	IContentProvider provider;
    	if (holder == null || holder.provider == null) {
    		Context c = null;
    		ApplicationInfo ai = info.applicationInfo;
    		if (context.getPackageName().equals(ai.packageName)) {
    			c = context;
    		} else if (mInitialApplication != null &&
    		           mInitialApplication.getPackageName().equals(ai.packageName)) {
    			c = mInitialApplication;
    		} else {
    			try {
    				c = context.createPackageContext(ai.packageName,
    				                                 Context.CONTEXT_INCLUDE_CODE);
    			} catch (PackageManager.NameNotFoundException e) {
    				// Ignore
    			}
    		}
    		if (c == null) {
    			return null;
    		}
    		try {
    			final java.lang.ClassLoader cl = c.getClassLoader();
    			localProvider = (ContentProvider)cl.
    			                loadClass(info.name).newInstance();
    			provider = localProvider.getIContentProvider();
    			if (provider == null) {
    				return null;
    			}
    			// XXX Need to create the correct context for this provider.
    			localProvider.attachInfo(c, info);
    		} catch (java.lang.Exception e) {......}
    	} else {
    		provider = holder.provider;
    	}
    
    	IActivityManager.ContentProviderHolder retHolder;
    
    	synchronized (mProviderMap) {
    		IBinder jBinder = provider.asBinder();
    		if (localProvider != null) {
    			ComponentName cname = new ComponentName(info.packageName, info.name);
    			ProviderClientRecord pr = mLocalProvidersByName.get(cname);
    			if (pr != null) {
    				provider = pr.mProvider;
    			} else {
    				holder = new IActivityManager.ContentProviderHolder(info);
    				holder.provider = provider;
    				holder.noReleaseNeeded = true;
    				pr = installProviderAuthoritiesLocked(provider, localProvider, holder);
    				mLocalProviders.put(jBinder, pr);
    				mLocalProvidersByName.put(cname, pr);
    			}
    			retHolder = pr.mHolder;
    		} else {
    			ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
    			if (prc != null) {
    				// We need to transfer our new reference to the existing
    				// ref count, releasing the old one...  but only if
    				// release is needed (that is, it is not running in the
    				// system process).
    				if (!noReleaseNeeded) {
    					incProviderRefLocked(prc, stable);
    					try {
    						ActivityManagerNative.getDefault().removeContentProvider(
    						    holder.connection, stable);
    					} catch (RemoteException e) {
    						//do nothing content provider object is dead any way
    					}
    				}
    			} else {
    				ProviderClientRecord client = installProviderAuthoritiesLocked(
    				                                provider, localProvider, holder);
    				if (noReleaseNeeded) {
    					prc = new ProviderRefCount(holder, client, 1000, 1000);
    				} else {
    					prc = stable
    					      ? new ProviderRefCount(holder, client, 1, 0)
    					      : new ProviderRefCount(holder, client, 0, 1);
    				}
    				mProviderRefCountMap.put(jBinder, prc);
    			}
    			retHolder = prc.holder;
    		}
    	}
    	return retHolder;
    }
    
    • 这步与前面的step20,都是在ActivityThread类成员函数installProvider()。不过,前面step20是在启动ContentProvider组件的应用程序进程中执行的;而这步是在AxxApp应用程序的MainActivity中执行的!另一个区别是:这步得到参数provider不等于null,它用来描述一个在其他应用程序进程中启动的ContentProvider组件的一个IContentProvider访问接口。

    • 这步执行完成后,就返回到前面的step1,这时在AxxApp应用的MainActivity中,就获得了与URI值对应的SubContentProvider组件的一个IContentProvider访问接口,最后就可以通过这个接口访问另一个应用程序的数据内容了!

    0x03 参考文献与简单的结语


    至此,ContentProvider启动流程分析到此结束!

  • 相关阅读:
    文件上传漏洞全面渗透姿势总结
    注册frpc为windows服务,可在未登录用户时启动
    SpringIOC 容器注入方式
    如何交换 Integer 的值?
    分布式websocket服务器
    win10安装Hbase2.3.0
    hadoop常用的命令
    win10安装kafka 2.122.8.1
    win10激活码
    win10 flume source为spooldir,输出到hdfs
  • 原文地址:https://www.cnblogs.com/wondertwo/p/5958163.html
Copyright © 2020-2023  润新知