现在的位置: 首页 > 综合 > 正文

Android 2.3 SD卡挂载流程浅析(六)

2013年10月11日 ⁄ 综合 ⁄ 共 23811字 ⁄ 字号 评论关闭

  前面五篇文章:

     《Android 2.3 SD卡挂载流程浅析(一)》

     《Android 2.3 SD卡挂载流程浅析(二)》

     《Android 2.3 SD卡挂载流程浅析(三)》

     《Android 2.3 SD卡挂载流程浅析(四)》

     《Android 2.3 SD卡挂载流程浅析(五)》

       在这五篇文章中,简单的分析了将SD卡插入的消息从底层传递到了上层的流程,但并没有深入分析毕竟我的目的是理清一条清晰的消息传递路线,最终目标是再系统的设置界面中显示出来,因此,本文将继续分析该流程。但是本文的分析又不同于前面五篇文章,因为本文是通过从上到下来分析,将分析流程反过来。

       1.首先找到系统设置的源码。

        路径:AndroidSorceCode2.3/package/app/Settings/src/com/android/settings/deviceinfo/Memory.java

        该文件其实就是我们打开系统设置-存储的一个界面,如下图:


     在设置中点击存储便进入到Memory的界面,如下:


       如果已经插入SD卡并且系统已经挂载了的话,这里会有显示。也就是说我们的最终目标在这里,SD卡的挂载信息是如何传递到这里的。我们继续回到Memory.java文件中。我们要如何知道一步该做什么呢?我们先在Eclipse中的logcat添加TAG名为Memory的TAG,然后插入SD卡,我们会发现有以下log输出:

这就是我们需要的关键点,因为这句log是从Memory.java中输出的,因此我们首先要找到该log的出处:

  1. <span style="font-size:18px;">    StorageEventListener mStorageListener = new StorageEventListener() {  
  2.   
  3.         @Override  
  4.         public void onStorageStateChanged(String path, String oldState, String newState) {  
  5.             Log.i(TAG, "Received storage state changed notification that " +  
  6.                     path + " changed state from " + oldState +  
  7.                     " to " + newState);  
  8.             updateMemoryStatus();  
  9.         }  
  10.     };</span>  


从以上代码中可以知道,这里就是输出关键log的地方,换句话说当我们插入SD卡的时候,系统触发这个onStroageStateChanged()方法,在该方法中一并执行了updateMemoryStatus()方法,我们跟踪进入updateMemoryStatus()方法看看,根据这名字我们大致可以猜测其作用是更新存储设备的状态信息:

  1. <span style="font-size:18px;">    private void updateMemoryStatus() {  
  2.         String status = Environment.getExternalStorageState();  
  3.         String readOnly = "";  
  4.         if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {  
  5.             status = Environment.MEDIA_MOUNTED;  
  6.             readOnly = mRes.getString(R.string.read_only);  
  7.         }  
  8.    
  9.         if (status.equals(Environment.MEDIA_MOUNTED)) {  
  10.             if (!Environment.isExternalStorageRemovable()) {  
  11.                 // This device has built-in storage that is not removable.
      
  12.                 // There is no reason for the user to unmount it.
      
  13.                 if (mSdMountToggleAdded) {  
  14.                     mSdMountPreferenceGroup.removePreference(mSdMountToggle);  
  15.                     mSdMountToggleAdded = false;  
  16.                 }  
  17.             }  
  18.             try {  
  19.                 File path = Environment.getExternalStorageDirectory();  
  20.                 StatFs stat = new StatFs(path.getPath());  
  21.                 long blockSize = stat.getBlockSize();  
  22.                 long totalBlocks = stat.getBlockCount();  
  23.                 long availableBlocks = stat.getAvailableBlocks();  
  24.                   
  25.                 mSdSize.setSummary(formatSize(totalBlocks * blockSize));  
  26.                 mSdAvail.setSummary(formatSize(availableBlocks * blockSize) + readOnly);  
  27.   
  28.                 mSdMountToggle.setEnabled(true);  
  29.                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_eject));  
  30.                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_eject_summary));  
  31.   
  32.             } catch (IllegalArgumentException e) {  
  33.                 // this can occur if the SD card is removed, but we haven't received the 
      
  34.                 // ACTION_MEDIA_REMOVED Intent yet.
      
  35.                 status = Environment.MEDIA_REMOVED;  
  36.             }  
  37.               
  38.         } else {  
  39.             mSdSize.setSummary(mRes.getString(R.string.sd_unavailable));  
  40.             mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));  
  41.   
  42.   
  43.             if (!Environment.isExternalStorageRemovable()) {  
  44.                 if (status.equals(Environment.MEDIA_UNMOUNTED)) {  
  45.                     if (!mSdMountToggleAdded) {  
  46.                         mSdMountPreferenceGroup.addPreference(mSdMountToggle);  
  47.                         mSdMountToggleAdded = true;  
  48.                     }  
  49.                 }  
  50.             }  
  51.   
  52.             if (status.equals(Environment.MEDIA_UNMOUNTED) ||  
  53.                 status.equals(Environment.MEDIA_NOFS) ||  
  54.                 status.equals(Environment.MEDIA_UNMOUNTABLE) ) {  
  55.                 mSdMountToggle.setEnabled(true);  
  56.                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));  
  57.                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));  
  58.             } else {  
  59.                 mSdMountToggle.setEnabled(false);  
  60.                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));  
  61.                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_insert_summary));  
  62.             }  
  63.         }  
  64.   
  65.         File path = Environment.getDataDirectory();  
  66.         StatFs stat = new StatFs(path.getPath());  
  67.         long blockSize = stat.getBlockSize();  
  68.         long availableBlocks = stat.getAvailableBlocks();  
  69.         findPreference("memory_internal_avail").setSummary(formatSize(availableBlocks * blockSize));  
  70.     }</span>  


果然不出我们所料,这里也就是真正更新设置-存储界面里面信息的方法。

       2.跟着源码中的Memory.java顺藤摸瓜

       我们回到StorageEventListener实例化对象的地方:

  1. <span style="font-size:18px;">    StorageEventListener mStorageListener = new StorageEventListener() {  
  2.   
  3.         @Override  
  4.         public void onStorageStateChanged(String path, String oldState, String newState) {  
  5.             Log.i(TAG, "Received storage state changed notification that " +  
  6.                     path + " changed state from " + oldState +  
  7.                     " to " + newState);  
  8.             updateMemoryStatus();  
  9.         }  
  10.     };</span>  


      通过代码我们可以知道,StorageEventListener是一个抽象类,在这里通过实例化自己的对象并用匿名内部类实现了自己定义中的抽象方法。我接着回到Memory.java中的onCreate()方法中:

  1. <span style="font-size:18px;">        if (mStorageManager == null) {  
  2.             mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);  
  3.             mStorageManager.registerListener(mStorageListener);  
  4.         }</span>  


      在这里我们可以看到,StorageEventListener的对象mStorageListener通过StorageManager的方法registerListener()完成注册。这里我们需要详细了解一下这个注册的过程,因为这里所谓的注册就为后面的触发埋下了伏笔,注册存储事件监听器(StorageEventListener)的目的就是为了在存储设备状态发生改变并触发事件的时候,接收并处理这些事件。

      (1).mStorageManager初始化

  1. <span style="font-size:18px;">mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);</span>  

      我直接跟踪getSystemService()方法,首先会跳转到Activity.java中的getSystemService()方法中:

  1. <span style="font-size:18px;">    @Override  
  2.     public Object getSystemService(String name) {  
  3.         if (getBaseContext() == null) {  
  4.             throw new IllegalStateException(  
  5.                     "System services not available to Activities before onCreate()");  
  6.         }  
  7.   
  8.         if (WINDOW_SERVICE.equals(name)) {  
  9.             return mWindowManager;  
  10.         } else if (SEARCH_SERVICE.equals(name)) {  
  11.             ensureSearchManager();  
  12.             return mSearchManager;  
  13.         }  
  14.         return super.getSystemService(name);  
  15.     }</span>  


     这里因为不满足if的判断条件,因此会返回调用父类的getSystemService方法。接下来继续跟踪到其父类中的getSystemService方法中查看,这里的Activity继承了ContextThemeWrapper这个类:

  1. <span style="font-size:18px;">    @Override public Object getSystemService(String name) {  
  2.         if (LAYOUT_INFLATER_SERVICE.equals(name)) {  
  3.             if (mInflater == null) {  
  4.                 mInflater = LayoutInflater.from(mBase).cloneInContext(this);  
  5.             }  
  6.             return mInflater;  
  7.         }  
  8.         return mBase.getSystemService(name);  
  9.     }</span>  


     根据if的判断条件来看,这里还是不会满足判断条件。如果这里我们继续点击getSystemService()方法去跟踪的话我们会发现,我们来到了一个抽象类Context类中。该类中的getSystemService()方法也是一个抽象方法,那么到这里我们已经无法分析了吗?非也非也。如果细心的话我们会发现这里的getSystemService()方法前面有一个mBase对象,该对象是Context的,因为抽象类不可能有自己的实例化对象,因此根据多态性可以知道,这里的mBase肯定是其子类的对象,因此我们需要找到该子类。

     (2)getSystemService()峰回路转

     我们首先看看这个mBase的定义,直接跳转过去可以看到:

  1. <span style="font-size:18px;">    private Context mBase;  
  2. ...  
  3.     public ContextThemeWrapper(Context base, int themeres) {  
  4.         super(base);  
  5.         mBase = base;  
  6.         mThemeResource = themeres;  
  7.     }  
  8.   
  9.     @Override protected void attachBaseContext(Context newBase) {  
  10.         super.attachBaseContext(newBase);  
  11.         mBase = newBase;  
  12.     }</span>  


      这里只截取了其中部分,但我已经可以看到给mBase赋值的地方有两处,这里该怎么断定呢?按照常理我们先去跟踪ContextThemeWrapper构造方法的调用处,直接在Eclipse对该方法点击右键,选择Open Call Hierarchy,这样就会出现调用该方法的地方,这样一步步跟踪下去似乎越来越乱,因为调转点实在是太多了,因此先就此打住。我们回过头先查看这里的attachBaseContext方法,通过同样的方法(因为自己也是第一次分析,很多东西都不懂,只能自己摸着石头过河,高手请勿见笑)。我们直接跳转会来到Activity中的attach()方法中:

  1. <span style="font-size:18px;">    final void attach(Context context, ActivityThread aThread,  
  2.             Instrumentation instr, IBinder token, int ident,  
  3.             Application application, Intent intent, ActivityInfo info,  
  4.             CharSequence title, Activity parent, String id,  
  5.             Object lastNonConfigurationInstance,  
  6.             HashMap<String,Object> lastNonConfigurationChildInstances,  
  7.             Configuration config) {  
  8.         attachBaseContext(context);//这里调用</span>  


       这里截取了部分代码,只抓取了我们需要的部分,这里发现如果调用了attach()方法的话会传递一个Context的对象,那么我们继续跟踪,在Activity的performLaunchActivity方法中我们发现了attach()方法的调用处:

  1. <span style="font-size:18px;">activity.attach(appContext, this, getInstrumentation(), r.token,  
  2.                         r.ident, app, r.intent, r.activityInfo, title, r.parent,  
  3.                         r.embeddedID, r.lastNonConfigurationInstance,  
  4.                         r.lastNonConfigurationChildInstances, config);</span>  


      通过以上代码我们可以发现这里传递了一个appContext参数,跟踪此参数,可以看到:

  1. <span style="font-size:18px;">ContextImpl appContext = new ContextImpl();</span>  


      原来是ContextImpl的对象,原来应该传入的对象是Context的,这里传入的却是ContextImpl的对象,因此不用想我们也知道,ContextImpl肯定是Context的子类,跟踪过去一看,果不其然:

  1. <span style="font-size:18px;">class ContextImpl extends Context</span>  


      既然ContextImpl继承了Context类,并将自己的对象作为参数传递进去,那么前面的mBase对象就应该是ContextImpl的对象,因此调用的getSystemService()方法也应该在ContextImpl类中有覆写。直接搜索可以找到:

  1. <span style="font-size:18px;">    @Override  
  2.     public Object getSystemService(String name) {  
  3.         if (WINDOW_SERVICE.equals(name)) {  
  4.             return WindowManagerImpl.getDefault();  
  5.         } else if (LAYOUT_INFLATER_SERVICE.equals(name)) {  
  6.             synchronized (mSync) {  
  7.                 LayoutInflater inflater = mLayoutInflater;  
  8.                 if (inflater != null) {  
  9.                     return inflater;  
  10.                 }  
  11.                 mLayoutInflater = inflater =  
  12.                     PolicyManager.makeNewLayoutInflater(getOuterContext());  
  13.                 return inflater;  
  14.             }  
  15.           
  16.         } else if (SENSOR_SERVICE.equals(name)) {  
  17.             return getSensorManager();  
  18.     ......  
  19.         } else if (STORAGE_SERVICE.equals(name)) {  
  20.             return getStorageManager();//这里是我们所需要的
      
  21.         } else if (USB_SERVICE.equals(name)) {  
  22.             return getUsbManager();  
  23.         } else if (VIBRATOR_SERVICE.equals(name)) {  
  24.             return getVibrator();  
  25.         }   
  26.     ......  
  27.         return null;  
  28.     }</span>  


      原来,我们梦里寻她千百度,蓦然回首,getSystemService()竟然藏在此处。因为我们在Memory.java中传递过来的是STORAGE_SERVICE,因此这里会执行getStorageManager()方法。

     (3).继续探索getStorageManager()

     继续跟踪getStorageManager()方法我们会看到:

  1. <span style="font-size:18px;">    private StorageManager getStorageManager() {  
  2.         synchronized (mSync) {  
  3.             if (mStorageManager == null) {  
  4.                 try {  
  5.                     mStorageManager = new StorageManager(mMainThread.getHandler().getLooper());  
  6.                 } catch (RemoteException rex) {  
  7.                     Log.e(TAG, "Failed to create StorageManager", rex);  
  8.                     mStorageManager = null;  
  9.                 }  
  10.             }  
  11.         }  
  12.         return mStorageManager;  
  13.     }</span>  


      通过该方法可以看到,返回的是一个StorageManager对象。但我们需要关注的是StorageManager(mMainThread.getHandler().getLooper())在这个方法中传递的参数是ActivityThread的handler中的looper。继续跟踪此方法就可以来到StorageManager的带参构造函数:

  1. <span style="font-size:18px;">    public StorageManager(Looper tgtLooper) throws RemoteException {  
  2.         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));  
  3.         if (mMountService == null) {  
  4.             Log.e(TAG, "Unable to connect to mount service! - is it running yet?");  
  5.             return;  
  6.         }  
  7.         mTgtLooper = tgtLooper;  
  8.         mBinderListener = new MountServiceBinderListener();  
  9.         mMountService.registerListener(mBinderListener);  
  10.     }</span>  


     
    在该方法中,首先初始化了IMountService的对象,因为目前自己对Binder这一块还不是很熟悉,因此只能凭借自己的理解来分析。我们先去看看ServiceManager.getService("mount")方法:

  1. <span style="font-size:18px;">    public static IBinder getService(String name) {  
  2.         try {  
  3.             IBinder service = sCache.get(name);  
  4.             if (service != null) {  
  5.                 return service;  
  6.             } else {  
  7.                 return getIServiceManager().getService(name);  
  8.             }  
  9.         } catch (RemoteException e) {  
  10.             Log.e(TAG, "error in getService", e);  
  11.         }  
  12.         return null;  
  13.     }</span>  


     
 “该方法将返回一个服务的引用,这个服务的名称就是我们传递进去的参数名称。如果这个服务不存在的话将返回null。”源码注释里面是这么说的,但我们就从代码中可以知道,实际上返回的是一个IBinder的对象。

        接着调用回到StorageManager的构造函数中的IMountService.Stub.asInterface()方法:

  1. <span style="font-size:18px;">        public static IMountService asInterface(IBinder obj) {  
  2.             if (obj == null) {  
  3.                 return null;  
  4.             }  
  5.             IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
  6.             if (iin != null && iin instanceof IMountService) {  
  7.                 return (IMountService) iin;  
  8.             }  
  9.             return new IMountService.Stub.Proxy(obj);  
  10.         }</span>  


       “该方法将一个IBinder对象转换成一个IMountService接口,如果必要的话将通过代理来实现”,这里所说的代理指的是Proxy()方法。

       这里又需要跳转到obj.queryLocaIInterface()方法中(PS:大家不要觉得枯燥,作为一个新手很多东西我也是第一次接触因此可能会走很多弯路,但过程还是很精彩的):

  1. <span style="font-size:18px;">public IInterface queryLocalInterface(String descriptor);</span>  


       很明显,这是一个在IBinder中的接口,因为Binder类实现了IBinder接口,因此我们直接去Binder类中查找该方法:

  1. <span style="font-size:18px;">    public IInterface queryLocalInterface(String descriptor) {  
  2.         if (mDescriptor.equals(descriptor)) {  
  3.             return mOwner;  
  4.         }  
  5.         return null;  
  6.     }</span>  


      以上方法的作用是,根据传入的描述符返回一个IInterface的mOwner对象,该mOwner对象在Binder类中有方法带参数传递如下:

  1. <span style="font-size:18px;">    public void attachInterface(IInterface owner, String descriptor) {  
  2.         mOwner = owner;  
  3.         mDescriptor = descriptor;  
  4.     }</span>  


       分析到这一步,我相信大家都头都晕了吧(不管你晕不晕,我反正是晕了,休息休息...)。
*************************************************分割线*********************************************************

       休息好了,我们继续分析吧。

       因为IInterface实际上也是一个接口,因此不可能实例化对象来传递,所以这里我们也不用想,只要找到其子类那么传递的对象就是其子类实例化的对象。但是要怎么找呢?我们这里的descriptor是“IMountService”因此我们可以在IMountService.java中寻找线索:

  1. <span style="font-size:18px;">public interface IMountService extends IInterface {  
  2.     /** Local-side IPC implementation stub class. */  
  3.     public static abstract class Stub extends Binder implements IMountService {  
  4.         private static class Proxy implements IMountService {  
  5.             private IBinder mRemote;  
  6.   
  7.             Proxy(IBinder remote) {  
  8.                 mRemote = remote;  
  9.             }  
  10. ...省略  
  11.   
  12.         /** Construct the stub at attach it to the interface. */  
  13.         public Stub() {  
  14.             attachInterface(this, DESCRIPTOR);  
  15.         }  
  16. ...省略</span>  


       这里可以看到IMountService继承了IInterface并且其内部类Stub还继承了Binder并实现了IMountService。(这里会涉及到了Android中的AIDL即Android Interface Defenition Language的知识,关于AIDL我会在博客中另起文章介绍并结合源码分析。
       虽然通过以上代码的分析,但似乎已经是死胡同了,那么接下来该肿么办呢?

      (4).切莫误入歧途
       我们回到StorageManager的带参构造函数中(别忘了我们从这里开始分支的):

  1. <span style="font-size:18px;">    public StorageManager(Looper tgtLooper) throws RemoteException {  
  2.         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));  
  3.         if (mMountService == null) {  
  4.             Log.e(TAG, "Unable to connect to mount service! - is it running yet?");  
  5.             return;  
  6.         }  
  7.         mTgtLooper = tgtLooper;  
  8.         mBinderListener = new MountServiceBinderListener();  
  9.         mMountService.registerListener(mBinderListener);  
  10.     }</span>