本文基于android 早期版本 可能是4.4
1. 相关代码目录:
android\system\core\libsysutils\src\FrameworkListener.cpp
android \system\vold
android \frameworks\base\services\java\com\android\server
2. SD卡开机加载方法和简易流程图
总结起来SD卡加载大概有3种方式:
1. 开机自动加载;
2. 热插拔;
如果手机支持热插拔,底层驱动会将SD卡驱动信息发送给VOLD,VolumeManager 或者Volume类会将处理状态发送给MountService,MountService最后发送状态给APP;
3. 通过setting菜单安装
/*卸载该分区挂载的所有挂载点,这里为什么用所有来形容了,因为Android 系统挂载一个分区的期间,重复挂载在好几个目录,将分区挂载在/mnt/asec目录,也挂载
在/mnt/secure/asec目录,也挂载在/mnt/sdcard目录下,总共三次挂载,谷歌不知为什么搞这么复杂?
待深究。。*/
这里介绍下开机自动加载的过程,以下是一个简单流程图:
源代码分析:
1. 开机
Vold为一守护进程,初始化时,会创建classNetlinkManager和VolumeManager,
Class NetlinkManager接收来自底层的信息,然后转交给VolumeManager处理;
class VolumeManager 单例模式,它主要负责vold的管理操作
class DirectVolume封装了很多的方法和属性;
class CommandListener内部封装了一个socket 用来跨进程通信,它在vold 进程中属于监听类,即服务器端,主要收到上层 MountService发来的命令,分析后,转交给VolumeManager处理;VolumeManager处理信息后,或报告给上层MountService
关于vold 的其他介绍请参考之前的《SD卡基本功能的原理实现和流程分析》。
system\core\init\init.c
Init .c通过加载Init.rc文件初始化Vold 模块和app_process模块,找到主函数main开始执行
service vold /system/bin/vold
class core
socket vold stream 0660 root mount
ioprio be 2
service zygote /system/bin/app_process-Xzygote /system/bin --zygote --start-system-server
class main
socket zygote stream 660 root system
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
找到主函数main开始执行,vold模块main 函数:
int main() {
VolumeManager *vm;
CommandListener *cl;
NetlinkManager *nm;
SLOGI("Vold 2.1 (the revenge) firing up");
mkdir("/dev/block/vold", 0755);
/* For when cryptfs checks and mounts an encrypted filesystem */
klog_set_level(6);
/* Create our singleton managers */
if (!(vm = VolumeManager::Instance())) {
SLOGE("Unable to create VolumeManager");
exit(1);
};
if (!(nm = NetlinkManager::Instance())) {
SLOGE("Unable to create NetlinkManager");
exit(1);
};
cl = new CommandListener();
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
if (vm->start()) {
SLOGE("Unable to start VolumeManager (%s)", strerror(errno));
exit(1);
}
if (process_config(vm)) {
SLOGE("Error reading configuration (%s)... continuing anyways",strerror(errno));
}
if (nm->start()) {
SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
exit(1);
}
coldboot("/sys/block");
// coldboot("/sys/class/switch");
/*
* Now that we're up, we can respond to commands
*/
if (cl->startListener()) {
SLOGE("Unable to start CommandListener (%s)",strerror(errno));
exit(1);
}
// Eventually we'll become the monitoring thread
while(1) {
sleep(1000);
}
SLOGI("Vold exiting");
exit(0);
}
app_main.cpp 主函数:
int main(int argc, char* const argv[])
{
#ifdef __arm__
/*
* b/7188322 - Temporarily revert to the compat memory layout
* to avoid breaking third party apps.
*
* THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE.
*
*http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466
* changes the kernel mapping from bottom up to top-down.
* This breaks some programs which improperly embed
* an out of date copy of Android's linker.
*/
char value[PROPERTY_VALUE_MAX];
property_get("ro.kernel.qemu", value, "");
bool is_qemu = (strcmp(value, "1") == 0);
if ((getenv("NO_ADDR_COMPAT_LAYOUT_FIXUP") == NULL) &&!is_qemu) {
int current = personality(0xFFFFFFFF);
if ((current & ADDR_COMPAT_LAYOUT) == 0) {
personality(current | ADDR_COMPAT_LAYOUT);
setenv("NO_ADDR_COMPAT_LAYOUT_FIXUP", "1", 1);
execv("/system/bin/app_process", argv);
return -1;
}
}
unsetenv("NO_ADDR_COMPAT_LAYOUT_FIXUP");
#endif
// ADDED BY MARS BEGIN
setconnecthook(android::hook_connect);
// ADDED BY MARS END
//These are global variables in ProcessState.cpp
mArgC = argc;
mArgV = argv;
mArgLen = 0;
for (int i=0; i mArgLen += strlen(argv[i]) + 1; } mArgLen--; AppRuntime runtime; const char* argv0 = argv[0]; // Process command line arguments // ignore argv[0] argc--; argv++; // Everything up to '--' or first non '-' arg goes to the vm int i = runtime.addVmArguments(argc, argv); // Parse runtime arguments. Stopat first unrecognized option. bool zygote = false; bool startSystemServer = false; bool application = false; const char* parentDir = NULL; const char* niceName = NULL; const char* className = NULL; while (i < argc) { const char* arg = argv[i++]; if (!parentDir) { parentDir = arg; } else if (strcmp(arg, "--zygote") == 0) { zygote = true; niceName = "zygote"; } else if (strcmp(arg, "--start-system-server") == 0) { startSystemServer = true; } else if (strcmp(arg, "--application") == 0) { application = true; } else if (strncmp(arg, "--nice-name=", 12) == 0) { niceName = arg + 12; } else { className = arg; break; } } if (niceName && *niceName) { setArgv0(argv0, niceName); set_process_name(niceName); } runtime.mParentDir = parentDir; if (zygote) { runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ?"start-system-server" : ""); } else if(className) { // Remainder of args get passed to startup class main() runtime.mClassName = className; runtime.mArgC = argc - i; runtime.mArgV = argv + i; runtime.start("com.android.internal.os.RuntimeInit", application ?"application" : "tool"); }else { fprintf(stderr, "Error: no class name or --zygotesupplied.\n"); app_usage(); LOG_ALWAYS_FATAL("app_process: no class name or --zygotesupplied."); return 10; } } ZygoteInit.java 找到主函数: public static void main(String argv[]) { try { // Start profiling the zygoteinitialization. SamplingProfilerIntegration.start(); registerZygoteSocket(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, SystemClock.uptimeMillis()); preload(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, SystemClock.uptimeMillis()); // Finish profiling the zygoteinitialization. SamplingProfilerIntegration.writeZygoteSnapshot(); // Do an initial gc to clean upafter startup gc(); // Disable tracing so that forkedprocesses do not inherit stale tracing tags from // Zygote. Trace.setTracingEnabled(false); // If requested, start systemserver directly from Zygote if (argv.length != 2) { throw newRuntimeException(argv[0] + USAGE_STRING); } if(argv[1].equals("start-system-server")) { startSystemServer(); //ADDED BY MARS BEGIN // startMrvlRootServer(); //ADDED BY MARS END } else if (!argv[1].equals("")) { throw newRuntimeException(argv[0] + USAGE_STRING); } Log.i(TAG, "Accepting commandsocket connections"); runSelectLoop(); closeServerSocket(); } catch (MethodAndArgsCaller caller) { caller.run(); } catch (RuntimeException ex) { Log.e(TAG, "Zygote died withexception", ex); closeServerSocket(); throw ex; } } private static boolean startSystemServer() throws MethodAndArgsCaller,RuntimeException { long capabilities =posixCapabilitiesAsBits( OsConstants.CAP_KILL, OsConstants.CAP_NET_ADMIN, OsConstants.CAP_NET_BIND_SERVICE, OsConstants.CAP_NET_BROADCAST, OsConstants.CAP_NET_RAW, OsConstants.CAP_SYS_MODULE, OsConstants.CAP_SYS_NICE, OsConstants.CAP_SYS_RESOURCE, OsConstants.CAP_SYS_TIME, OsConstants.CAP_SYS_TTY_CONFIG ); /* Hardcoded command line to start thesystem server */ String args[] = { "--setuid=1000", "--setgid=1000", "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007", "--capabilities=" +capabilities + "," + capabilities, "--runtime-init", "--nice-name=system_server", "com.android.server.SystemServer", }; ZygoteConnection.Arguments parsedArgs =null; int pid; try { parsedArgs = newZygoteConnection.Arguments(args); ZygoteConnection.applyDebuggerSystemProperty(parsedArgs); ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs); /* Requestto fork the system server process */ pid =Zygote.forkSystemServer( parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, null, parsedArgs.permittedCapabilities, parsedArgs.effectiveCapabilities); } catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } /* For child process */ if (pid == 0) { handleSystemServerProcess(parsedArgs); } return true; } SystemServer.java public static void main(String[] args) { SystemProperties.set("persist.sys.dalvik.vm.lib", VMRuntime.getRuntime().vmLibrary()); if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) { // If a device's clock is before 1970 (before 0), a lot of // APIs crash dealing with negative numbers, notably // java.io.File#setLastModified, so instead we fake it and // hope that time from cell towers or NTP fixes it // shortly. Slog.w(TAG, "System clock is before 1970; setting to 1970."); SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME); } if (SamplingProfilerIntegration.isEnabled()) { SamplingProfilerIntegration.start(); timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { SamplingProfilerIntegration.writeSnapshot("system_server",null); } }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL); } // Mmmmmm... more memory! dalvik.system.VMRuntime.getRuntime().clearGrowthLimit(); // The system server has to run all of the time, so it needs to be // as efficient as possible with its memory usage. VMRuntime.getRuntime().setTargetHeapUtilization(0.8f); Environment.setUserRequired(true); System.loadLibrary("android_servers"); Slog.i(TAG, "Entered the Android systemserver!"); // Initialize native services. nativeInit(); // This used to be its own separate thread, but now it is // just the loop we run on the main thread. ServerThread thr = new ServerThread(); thr.initAndLoop(); } initAndLoop函数代码比较长,它会初始化系统的service, 当然也包括我们关心的MountService: 先构造函数: mountService = new MountService(context); 然后调用ActivityManagerService.self().systemReady // We now tell the activity manager itis okay to run third party // code. It will call back into us once it has gottento the state // where third party code can reallyrun (but before it has actually // started launching the initialapplications), for us to complete our // initialization. ActivityManagerService.self().systemReady(newRunnable() { public void run() { Slog.i(TAG, "Makingservices ready"); try { ActivityManagerService.self().startObservingNativeCrashes(); } catch (Throwable e) { reportWtf("observingnative crashes", e); } if (!headless) { startSystemUi(contextF); } try { if(mountServiceF != null) mountServiceF.systemReady(); } catch(Throwable e) { reportWtf("making Mount Service ready", e); } MountService 构造函数: public MountService(Context context) { mContext = context; synchronized (mVolumesLock) { readStorageListLocked(); } // XXX: This will go away soon in favorof IMountServiceObserver mPms = (PackageManagerService)ServiceManager.getService("package"); 新建一个重要的HandlerThread,用来处理一些重要的消息,主要处理开机和卸载的时候的一些消息 HandlerThreadhthread = new HandlerThread(TAG); hthread.start(); mHandler = newMountServiceHandler(hthread.getLooper()); // Watch for user changes final IntentFilter userFilter = newIntentFilter(); userFilter.addAction(Intent.ACTION_USER_ADDED); userFilter.addAction(Intent.ACTION_USER_REMOVED); mContext.registerReceiver(mUserReceiver,userFilter, null, mHandler); // Watch for USB changes on availablevolumes StorageVolume[] volumes =getVolumeList(); boolean allowUMS = false; for (StorageVolume volume : volumes) { if (volume != null &&volume.allowMassStorage()) { allowUMS = true; break; } } if (allowUMS) { mContext.registerReceiver(mUsbReceiver, newIntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler); } // Watch for idle maintenance changes IntentFilter idleMaintenanceFilter =new IntentFilter(); idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START); mContext.registerReceiverAsUser(mIdleMaintenanceReceiver,UserHandle.ALL, idleMaintenanceFilter, null,mHandler); // Add OBB Action Handler toMountService thread. mObbActionHandler = newObbActionHandler(IoThread.get().getLooper()); /* * Create the connection to vold with amaximum queue of twice the * amount of containers we'd everexpect to have. This keeps an * "asec list" from blockinga thread repeatedly. */ mConnector =new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG,25); Thread thread = new Thread(mConnector,VOLD_TAG); thread.start(); 这里启用了一个新线程,最终会在listenToSocke()函数中回调到onDaemonConnected(), // Add ourself to the Watchdog monitorsif enabled. if (WATCHDOG_ENABLE) { Watchdog.getInstance().addMonitor(this); } } //构造函数 NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, Stringsocket, int responseQueueSize, StringlogTag, int maxLogSize) { mCallbacks = callbacks; mSocket = socket; mResponseQueue = newResponseQueue(responseQueueSize); mSequenceNumber = new AtomicInteger(0); TAG = logTag != null ? logTag :"NativeDaemonConnector"; mLocalLog = new LocalLog(maxLogSize); } @Override public void run() { mCallbackHandler = newHandler(FgThread.get().getLooper(), this); while (true) { try { //开始监听来自于底层vold部分的消息 listenToSocket(); } catch (Exception e) { loge("Error inNativeDaemonConnector: " + e); SystemClock.sleep(5000); } } } public void onDaemonConnected() { /* * Since we'll be calling back into theNativeDaemonConnector, * we need to do our work in a newthread. */ newThread("MountService#onDaemonConnected") { @Override public void run() { /** * Determine media state andUMS detection status */ try { //给vold下发volume list命令,先列出系统目前有几个volume对象,获取到这些对象的标签 final String[] vols =NativeDaemonEvent.filterMessageList( mConnector.executeForList("volume", "list"), VoldResponseCode.VolumeListResult); for (String volstr : vols){ String[] tok =volstr.split(" "); // FMT: String path = tok[1]; String state =Environment.MEDIA_REMOVED; final StorageVolumevolume; synchronized(mVolumesLock) { volume =mVolumesByPath.get(path); } …… } MountService.java public void systemReady() { mSystemReady = true; mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget(); } 此处通过向MountServiceHandler发送H_SYSTEM_READY 消息来调用handleSystemReady()函数: private void handleSystemReady() { // Snapshot current volume states sinceit's not safe to call into vold // while holding locks. final HashMap synchronized (mVolumesLock) { snapshot = new HashMap } for (Map.Entry final String path = entry.getKey(); final String state =entry.getValue(); if(state.equals(Environment.MEDIA_UNMOUNTED)) { int rc = doMountVolume(path); if (rc !=StorageResultCode.OperationSucceeded) { Slog.e(TAG,String.format("Boot-time mount failed (%d)", rc)); } } else if(state.equals(Environment.MEDIA_SHARED)) { /* * Bootstrap UMS enabled statesince vold indicates * the volume is shared(runtime restart while ums enabled) */ notifyVolumeStateChange(null,path, VolumeState.NoMedia, VolumeState.Shared); } } // Push mounted state for all emulatedstorage synchronized (mVolumesLock) { for (StorageVolume volume :mVolumes) { if (volume.isEmulated()) { updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED); } } } /* * If UMS was connected on boot, sendthe connected event * now that we're up. */ if (mSendUmsConnectedOnBoot) { sendUmsIntent(true); mSendUmsConnectedOnBoot = false; } } doMountVolume就是之前讲过的挂载函数,NativeDaemonConnector 和CommandListener 作为桥梁将volume mount 命令发送给vold 模块的另一大类VolumeManager来执行相应的挂载操作,挂载完成后会将相应的状态信息再通过这个桥梁发送给Mountservice中的onEvent,于是执行以下函数: private void notifyVolumeStateChange(Stringlabel, String path, int oldState, int newState) { final StorageVolume volume; final String state; synchronized (mVolumesLock) { volume = mVolumesByPath.get(path); state = getVolumeState(path); } if (DEBUG_EVENTS) Slog.i(TAG,"notifyVolumeStateChange::" + state); String action = null; if (oldState == VolumeState.Shared&& newState != oldState) { if (LOCAL_LOGD) Slog.d(TAG,"Sending ACTION_MEDIA_UNSHARED intent"); sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL); } if (newState == VolumeState.Init) { } else if (newState ==VolumeState.NoMedia) { // NoMedia is handled via DiskRemove events } else if (newState ==VolumeState.Idle) { /* * Don't notify if we're inBAD_REMOVAL, NOFS, UNMOUNTABLE, or * if we're in the process of enablingUMS */ if (!state.equals( Environment.MEDIA_BAD_REMOVAL) && !state.equals( Environment.MEDIA_NOFS) && !state.equals( Environment.MEDIA_UNMOUNTABLE)&& !getUmsEnabling()) { if (DEBUG_EVENTS) Slog.i(TAG,"updating volume state for media bad removal nofs and unmountable"); updatePublicVolumeState(volume,Environment.MEDIA_UNMOUNTED); action =Intent.ACTION_MEDIA_UNMOUNTED; } } else if (newState ==VolumeState.Pending) { } else if (newState ==VolumeState.Checking) { if (DEBUG_EVENTS) Slog.i(TAG,"updating volume state checking"); updatePublicVolumeState(volume,Environment.MEDIA_CHECKING); action =Intent.ACTION_MEDIA_CHECKING; } else if(newState == VolumeState.Mounted) { if(DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted"); updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED); action =Intent.ACTION_MEDIA_MOUNTED; } else if (newState == VolumeState.Unmounting) { action = Intent.ACTION_MEDIA_EJECT; } 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 eventfirst */ updatePublicVolumeState(volume,Environment.MEDIA_UNMOUNTED); sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume,UserHandle.ALL); if (DEBUG_EVENTS) Slog.i(TAG,"Updating media shared"); updatePublicVolumeState(volume,Environment.MEDIA_SHARED); action =Intent.ACTION_MEDIA_SHARED; if (LOCAL_LOGD) Slog.d(TAG,"Sending ACTION_MEDIA_SHARED intent"); } else if (newState ==VolumeState.SharedMnt) { Slog.e(TAG, "Live shared mountsnot supported yet!"); return; } else { Slog.e(TAG, "UnhandledVolumeState {" + newState + "}"); } if (action != null) { sendStorageIntent(action,volume, UserHandle.ALL); } } private void sendStorageIntent(Stringaction, StorageVolume volume, UserHandle user) { final Intent intent = newIntent(action, Uri.parse("file://" + volume.getPath())); intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, volume); Slog.d(TAG, "sendStorageIntent "+ intent + " to " + user); mContext.sendBroadcastAsUser(intent,user); } 3. 关机卸载SD卡部分 MountServiceHandler 接收消息顺序: 1 H_UNMOUNT_PM_UPDATE 2 H_UNMOUNT_PM_DONE 3 H_UNMOUNT_MS 关机确认后,启动关机线程ShutdownThread的run 函数, public void run() { …… synchronized (mActionDoneSync) { try { final IMountService mount =IMountService.Stub.asInterface( ServiceManager.checkService("mount")); if(mount != null) { mount.shutdown(observer); } else{ Log.w(TAG, "MountService unavailable for shutdown"); } } catch (Exception e) { Log.e(TAG, "Exceptionduring MountService shutdown", e); } while (!mActionDone) { long delay = endShutTime -SystemClock.elapsedRealtime(); if (delay <= 0) { Log.w(TAG, "Shutdownwait timed out"); break; } try { mActionDoneSync.wait(delay); } catch (InterruptedExceptione) { } } } …… } 这里调用到MountService 中的shutdown 函数: public void shutdown(final IMountShutdownObserver observer) { validatePermission(android.Manifest.permission.SHUTDOWN); Slog.i(TAG, "Shutting down"); synchronized (mVolumesLock) { for (String path : mVolumeStates.keySet()) { String state = mVolumeStates.get(path); if(state.equals(Environment.MEDIA_SHARED)) { /* * If the media is currently shared, unshareit. * XXX: This is stilldangerous!. We should not * be rebooting at *all* ifUMS is enabled, since * the UMS host could havedirty FAT cache entries * yet to flush. */ setUsbMassStorageEnabled(false); } else if(state.equals(Environment.MEDIA_CHECKING)) { /* * If the media is being checked,then we need to wait for * it to complete beforebeing able to proceed. */ // XXX: @hackbod - Shouldwe disable the ANR timer here? int retries = 30; while(state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) { try { Thread.sleep(1000); } catch(InterruptedException iex) { Slog.e(TAG, "Interruptedwhile waiting for media", iex); break; } state =Environment.getExternalStorageState(); } if (retries == 0) { Slog.e(TAG, "Timedout waiting for media to check"); } } if (state.equals(Environment.MEDIA_MOUNTED)){ // Post a unmount message. ShutdownCallBack ucb = newShutdownCallBack(path, observer); mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb)); } elseif (observer != null) { /* * Observer is waiting foronShutDownComplete when we are done. * Since nothing will bedone send notification directly so shutdown * sequence can continue. */ try { observer.onShutDownComplete(StorageResultCode.OperationSucceeded); } catch (RemoteException e){ Slog.w(TAG,"RemoteException when shutting down"); } } } } } 通过给MountServiceHandler发送H_UNMOUNT_PM_UPDATE消息调用PM中的updateExternalMediaStatus函数来更新外部媒体状态,PM中unloadAllContainers();会先调用SecureContainer 卸载安全目录(如果有挂载该安全目录), 处理完成后调用MountService中的finishMediaUpdate()来给MountServiceHandler再发送 H_UNMOUNT_PM_DONE,H_UNMOUNT_MS消息来杀死运行中的进程, 最后调用到doUnmountVolume 函数,和挂载时一样通过给vold 发送卸载命令来卸载SD卡。 可以看到安卓为了模块间的独立大量使用了轻量级的IPC binder 和一些线程间消息的传递。 NetlinkManager 中监听Linux内核的热插拔事件,uevent事件 void NetlinkHandler::onEvent(NetlinkEvent*evt) { VolumeManager *vm = VolumeManager::Instance(); const char *subsys = evt->getSubsystem(); if (!subsys) { SLOGW("No subsystem found in netlink event"); return; } if (!strcmp(subsys, "block")) { vm->handleBlockEvent(evt); } int DirectVolume::handleBlockEvent(NetlinkEvent*evt) 对以下六种磁盘的操作动作做出判断 void handleDiskAdded(const char *devpath,NetlinkEvent *evt); void handleDiskRemoved(const char *devpath,NetlinkEvent *evt); void handleDiskChanged(const char *devpath,NetlinkEvent *evt); void handlePartitionAdded(const char*devpath, NetlinkEvent *evt); void handlePartitionRemoved(const char*devpath, NetlinkEvent *evt); void handlePartitionChanged(const char*devpath, NetlinkEvent *evt); 主要用来处理存储卡插入移除和SD卡改变,以及一些分区的处理,同时发送状态的改变信息给MountService. 再由MountService 来下发相应的命令给vold中的VolumeManager来做相应的处理。 1. Socket 通信 未完。。。
4. 热插拔SD卡简介