android usb挂载分析---FrameWork层处理vold消息

从前面的知识我们看到,在vold层收到 FrameWork层的消息后,会进行相应的处理,同时在处理的过程中会上报相应的状态给FrameWork层,在这个过程中主要上报了两种消息:

1、开始挂载前上报State_Checking消息。

2、挂载成功后上报State_Mounted消息。

针对这两个消息,我们看下FrameWork层相应的处理,这两个消息处理的流程基本差不多,只是对于State_Mounted在处理的时候多了一个updateExternalMediaStatus通知PackageManagerService进行相应的更新,所以我们把两个的状态处理都在同一个图中画出来了:

首先还是阻塞在NativeDaemonConnector中的listenToSocket等待vold层消息的到来,收到消息后,调用onEvent函数进行处理,这里收到的这两个消息类型都是VolumeStateChange,所以调用notifyVolumeStateChange函数

    private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
        String vs = getVolumeState(path);
        if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);

        Intent in = null;

        if (oldState == VolumeState.Shared && newState != oldState) {
            if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
            mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
                                                Uri.parse("file://" + path)));
        }

        if (newState == VolumeState.Init) {
        } else if (newState == VolumeState.NoMedia) {
            // NoMedia is handled via Disk Remove events
        } else if (newState == VolumeState.Idle) {
            /*
             * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
             * if we're in the process of enabling UMS
             */
            if (!vs.equals(
                    Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
                            Environment.MEDIA_NOFS) && !vs.equals(
                                    Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
                if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
                updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
            }
        } else if (newState == VolumeState.Pending) {
        } else if (newState == VolumeState.Checking) {
            if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
            updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
            in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
        } else if (newState == VolumeState.Mounted) {
            if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
            updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
            in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
            in.putExtra("read-only", false);
        } else if (newState == VolumeState.Unmounting) {
            in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
        } else if (newState == VolumeState.Formatting) {
        } else if (newState == VolumeState.Shared) {
            if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
            /* Send the media unmounted event first */
            updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
            in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
            mContext.sendBroadcast(in);

            if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
            updatePublicVolumeState(path, Environment.MEDIA_SHARED);
            in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
            if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
        } else if (newState == VolumeState.SharedMnt) {
            Slog.e(TAG, "Live shared mounts not supported yet!");
            return;
        } else {
            Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
        }

        if (in != null) {
            mContext.sendBroadcast(in);
        }
    }
从代码我们可以看到对 Checking和Mount两类消息都是调用updatePublicVolumeState进行处理,我们看下这个函数:

    private void updatePublicVolumeState(String path, String state) {
        if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
            Slog.w(TAG, "Multiple volumes not currently supported");
            return;
        }

        if (mLegacyState.equals(state)) {
            Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
            return;
        }

        if (Environment.MEDIA_UNMOUNTED.equals(state)) {
            // Tell the package manager the media is gone.
            mPms.updateExternalMediaStatus(false, false);

            /*
             * Some OBBs might have been unmounted when this volume was
             * unmounted, so send a message to the handler to let it know to
             * remove those from the list of mounted OBBS.
             */
            mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
                    path));
        } else if (Environment.MEDIA_MOUNTED.equals(state)) {
            // Tell the package manager the media is available for use.
            mPms.updateExternalMediaStatus(true, false);
        }

        String oldState = mLegacyState;
        mLegacyState = state;

        synchronized (mListeners) {
            for (int i = mListeners.size() -1; i >= 0; i--) {
                MountServiceBinderListener bl = mListeners.get(i);
                try {
                    bl.mListener.onStorageStateChanged(path, oldState, state);
                } catch (RemoteException rex) {
                    Slog.e(TAG, "Listener dead");
                    mListeners.remove(i);
                } catch (Exception ex) {
                    Slog.e(TAG, "Listener failed", ex);
                }
            }
        }
    }
针对Mount消息会调用updateExternalMediaStatus去更新PackageManagerService中的一些信息,这个我们一会再讲,接着往下看,调用onStorageStateChanged通知SDCARD状态改变,这个listener是哪里来的呢,我们跟踪源码可以看到是MountServiceBinderListener的一个成员变量,其类型是IMountServiceListener,IMountServiceListener是一个接口,我们跟踪其实现,最后可以看到是在StorageManager的MountServiceBinderListener的这个类继承了IMountServiceListener这个接口,而且我们看StorageManager的构造函数:
   
    public StorageManager(Looper tgtLooper) throws RemoteException {
        mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
        if (mMountService == null) {
            Log.e(TAG, "Unable to connect to mount service! - is it running yet?");
            return;
        }
        mTgtLooper = tgtLooper;
        mBinderListener = new MountServiceBinderListener();
        mMountService.registerListener(mBinderListener);
    }

对的,就是在它的构造函数中实例化了一个listener并注册到MountService中,回到上面,调用了listener的onStorageStateChanged,这里通过binder最终调用了StorageManager类中MountServiceBinderListener的onStorageStateChanged:
        public void onStorageStateChanged(String path, String oldState, String newState) {
            final int size = mListeners.size();
            for (int i = 0; i < size; i++) {
                mListeners.get(i).sendStorageStateChanged(path, oldState, newState);
            }
        }
这里的mListeners是一个List,其中的成员是ListenerDelegate类,所以这里调用了ListenerDelegate的sendStorageStateChanged方法

 void sendStorageStateChanged(String path, String oldState, String newState) {
            StorageStateChangedStorageEvent e = new StorageStateChangedStorageEvent(path, oldState, newState);
            mHandler.sendMessage(e.getMessage());
        }
这里只是简单的发一条StorageStateChangedStorageEvent 消息,我们看看下这个Handle 的处理函数,在ListenerDelegate类中重写了handleMessage方法:

 public void handleMessage(Message msg) {
                    StorageEvent e = (StorageEvent) msg.obj;

                    if (msg.what == StorageEvent.EVENT_UMS_CONNECTION_CHANGED) {
                        UmsConnectionChangedStorageEvent ev = (UmsConnectionChangedStorageEvent) e;
                        mStorageEventListener.onUsbMassStorageConnectionChanged(ev.available);
                    } else if (msg.what == StorageEvent.EVENT_STORAGE_STATE_CHANGED) {
                        StorageStateChangedStorageEvent ev = (StorageStateChangedStorageEvent) e;
                        mStorageEventListener.onStorageStateChanged(ev.path, ev.oldState, ev.newState);
                    } else {
                        Log.e(TAG, "Unsupported event " + msg.what);
                    }
                }
            };

这里调用mStorageEventListener的onStorageStateChanged,这里mStorageEventListener是StorageEventListener类型的。

一般应用需要在sd卡状态改变的时候做一些处理也就只要继承StorageEventListener并重写mStorageEventListener这个方法,然后把这个Listener  调用StorageManager 的registerListener注册进来,这样在sdcard状态变化的时候就能收到消息了,进行处理了。

好了,对于vold的发的这两个消息的处理就差不多了 State_Checking和State_Mounted处理的流程基本都是一样的,只是对于State_Mounted消息还多了一个和PackageManageService打交道的过程,我们最后来看下这个交互过程都做了什么:

   /*
    * Update media status on PackageManager.
    */
   public void updateExternalMediaStatus(final boolean mediaStatus, final boolean reportStatus) {
       if (Binder.getCallingUid() != Process.SYSTEM_UID) {
           throw new SecurityException("Media status can only be updated by the system");
       }
       synchronized (mPackages) {
           Log.i(TAG, "Updating external media status from " +
                   (mMediaMounted ? "mounted" : "unmounted") + " to " +
                   (mediaStatus ? "mounted" : "unmounted"));
           if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
                   mediaStatus+", mMediaMounted=" + mMediaMounted);
           if (mediaStatus == mMediaMounted) {
               Message msg = mHandler.obtainMessage(UPDATED_MEDIA_STATUS,
                       reportStatus ? 1 : 0, -1);
               mHandler.sendMessage(msg);
               return;
           }
           mMediaMounted = mediaStatus;
       }
       // Queue up an async operation since the package installation may take a little while.
       mHandler.post(new Runnable() {
           public void run() {
               mHandler.removeCallbacks(this);
               updateExternalMediaStatusInner(mediaStatus, reportStatus);
           }
       });
   }

这里主要是调用updateExternalMediaStatusInner,这里面又主要调用loadMediaPackages函数,我们看下这个函数:

   /*
    * Look at potentially valid container ids from processCids
    * If package information doesn't match the one on record
    * or package scanning fails, the cid is added to list of
    * removeCids. We currently don't delete stale containers.
    */
   private void loadMediaPackages(HashMap<SdInstallArgs, String> processCids,
           int uidArr[], HashSet<String> removeCids) {
       ArrayList<String> pkgList = new ArrayList<String>();
       Set<SdInstallArgs> keys = processCids.keySet();
       boolean doGc = false;
       for (SdInstallArgs args : keys) {
           String codePath = processCids.get(args);
           if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading container : "
                   + args.cid);
           int retCode = PackageManager.INSTALL_FAILED_CONTAINER_ERROR;
           try {
               // Make sure there are no container errors first.
               if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED)
                       != PackageManager.INSTALL_SUCCEEDED) {
                   Slog.e(TAG, "Failed to mount cid : " + args.cid +
                   " when installing from sdcard");
                   continue;
               }
               // Check code path here.
               if (codePath == null || !codePath.equals(args.getCodePath())) {
                   Slog.e(TAG, "Container " + args.cid + " cachepath " + args.getCodePath()+
                           " does not match one in settings " + codePath);
                   continue;
               }
               // Parse package
               int parseFlags = PackageParser.PARSE_ON_SDCARD | mDefParseFlags;
               doGc = true;
               synchronized (mInstallLock) {
                   final PackageParser.Package pkg =  scanPackageLI(new File(codePath),
                           parseFlags, 0, 0);
                   // Scan the package
                   if (pkg != null) {
                       synchronized (mPackages) {
                           retCode = PackageManager.INSTALL_SUCCEEDED;
                           pkgList.add(pkg.packageName);
                           // Post process args
                           args.doPostInstall(PackageManager.INSTALL_SUCCEEDED);
                       }
                   } else {
                       Slog.i(TAG, "Failed to install pkg from  " +
                               codePath + " from sdcard");
                   }
               }

           } finally {
               if (retCode != PackageManager.INSTALL_SUCCEEDED) {
                   // Don't destroy container here. Wait till gc clears things up.
                   removeCids.add(args.cid);
               }
           }
       }
       synchronized (mPackages) {
           // If the platform SDK has changed since the last time we booted,
           // we need to re-grant app permission to catch any new ones that
           // appear.  This is really a hack, and means that apps can in some
           // cases get permissions that the user didn't initially explicitly
           // allow...  it would be nice to have some better way to handle
           // this situation.
           final boolean regrantPermissions = mSettings.mExternalSdkPlatform
                   != mSdkVersion;
           if (regrantPermissions) Slog.i(TAG, "Platform changed from "
                   + mSettings.mExternalSdkPlatform + " to " + mSdkVersion
                   + "; regranting permissions for external storage");
           mSettings.mExternalSdkPlatform = mSdkVersion;
           
           // Make sure group IDs have been assigned, and any permission
           // changes in other apps are accounted for
           updatePermissionsLP(null, null, true, regrantPermissions, regrantPermissions);
           // Persist settings
           mSettings.writeLP();
       }
       // Send a broadcast to let everyone know we are done processing
       if (pkgList.size() > 0) {
           sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
       }
       // Force gc to avoid any stale parser references that we might have.
       if (doGc) {
           Runtime.getRuntime().gc();
       }
       // List stale containers and destroy stale temporary containers.
       if (removeCids != null) {
           for (String cid : removeCids) {
               if (cid.startsWith(mTempContainerPrefix)) {
                   Log.i(TAG, "Destroying stale temporary container " + cid);
                   PackageHelper.destroySdDir(cid);
               } else {
                   Log.w(TAG, "Container " + cid + " is stale");
               }
           }
       }
   }
这里面主要是调用scanPackageLI对sd卡里面的东西进行处理。














  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
.NET Framework 3.5镜像是指一种用于安装.NET Framework 3.5的文件副本。它通常用于在没有互联网连接或无法通过互联网下载.NET Framework的环境中进行安装。 .NET Framework是一种由微软开发的应用程序开发框架,为开发和运行Windows上的应用程序提供了丰富的库和工具。在安装.NET Framework时,通常需要从微软的官方网站下载安装程序,并进行安装。但是,有时候我们可能需要在离线环境中安装.NET Framework,这时候就需要使用.NET Framework 3.5镜像。 .NET Framework 3.5镜像是一个包含了.NET Framework 3.5的安装文件的副本。它可以在一个可移动存储设备上提供,例如光盘、USB闪存驱动器或硬盘。当我们需要在没有网络连接的计算机上安装.NET Framework 3.5时,只需将镜像文件复制到目标计算机上,并运行其中的安装程序。这样,我们就能在没有网络连接的环境中成功安装.NET Framework 3.5。 为了获得.NET Framework 3.5镜像,我们可以通过访问微软的官方网站或其他受信任的网站来下载官方的离线安装程序。这个安装程序将包含完整的.NET Framework 3.5软件包,并包含在一个可执行文件中。我们可以将这个可执行文件保存到一个存储设备中,作为我们的.NET Framework 3.5镜像。 总的来说,.NET Framework 3.5镜像是一个用来在离线环境中安装.NET Framework 3.5的文件副本。它提供了一种便捷的方式来安装.NET Framework,而无需依赖互联网连接。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值