android 4.0 sdcard 内外切换

本文介绍了一种在Android系统中实现内部存储和外部SD卡切换的方法。通过修改vold.fstab配置文件并重启vold进程,达到动态切换内外SD卡的目的。涉及的关键步骤包括配置文件读取、进程重启及重新挂载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇sdcard 内外切换只是外部的链接改变而已,其实没什么用。
这次,是真正的内外卡切换。
大概思路:
vold进程启动时会读取vold.fstab文件,此文件记录了要挂载分区的信息。要想真正切换,就要改这个文件,把分区信息调换一下,然后使vold重启。
如何重启,没错,就是 kill pid了。不过不能在自己的进程中执行kill pid命令,MountService 会不起作用。
只好选个同级的进程 netd。
具体操作:
1.设置属性来读配置文件vold.fstab。
/system/vold/main.cpp
static int process_config(VolumeManager *vm) {
    FILE *fp;
    int n = 0;
    int i = 0;
    char line[255];


    
    char mount[10];
property_get("persist.sys.sa.mount", mount, NULL);
LOGD("----persist.sys.sa.mount: %s", mount);
if (!strcmp(mount, "1")) {
if (!(fp = fopen("/etc/vold2.fstab", "r"))) {
return -1;
}
} else {
if (!(fp = fopen("/etc/vold.fstab", "r"))) {
return -1;
}
}

       ...
}
2 点击UI切换时,通过netd执行kill pid,杀死vold,使其重启。
Memory.java
// 获取network服务
private synchronized INetworkManagementService getNetworkManagementService() {
if (mNetworkService == null) {
IBinder service = ServiceManager
.getService(Context.NETWORKMANAGEMENT_SERVICE);
if (service != null) {
mNetworkService = INetworkManagementService.Stub
.asInterface(service);
} else {
Log.e(TAG, "Can't get network_management service");
}
}
return mNetworkService;
}


// vold重启后,sdcard会unmount掉,所以这里重新mount上
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
IMountService mountService = getMountService();
try {
for (StorageVolume sv : volumeList) {
mountService.mountVolume(sv.getPath());
}
} catch (RemoteException e) {
e.printStackTrace();
} finally {
if (progressDialog != null) {
progressDialog.dismiss();
progressDialog = null;
}
}
}
};
    
    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
    final String key = preference.getKey();
    if (EXTERNAL_STORAGE_CHOICE.equals(key)) {
int value = Integer.parseInt((String) newValue);
if (oldValue == value) {
return false;
}
progressDialog = ProgressDialog.show(getActivity(), "",
(String)mResources.getText(R.string.switch_sdcard), true, false);
oldValue = value;
switched = true;
storageChoicePreference.setSummary(summary[value]);

Settings.System.putInt(getContentResolver(),
Settings.System.EXTERNAL_STORAGE, value);




SystemProperties.set("persist.sys.sa.mount", (String)newValue);

IMountService mountService = getMountService();

for (StorageVolume sv : volumeList) {
String path = sv.getPath();
try {
String state = mountService.getVolumeState(path);
if (state.equals(Environment.MEDIA_MOUNTED)) {
len ++;

// onStorageStateChanged sendbroadcast to kill vold

//在killvold之前,确保unmount 所有volume

mountService.unmountVolume(path, true, false);
}

} catch (RemoteException e) {
e.printStackTrace();
}
}

    }
    return true;
    }
 
StorageEventListener mStorageListener = new StorageEventListener() {
        @Override
        public void onStorageStateChanged(String path, String oldState, String newState) {
            Log.i(TAG, "Received storage state changed notification that " + path +
                    " changed state from " + oldState + " to " + newState);
            
            if (switched) {
            len --;
            if (len == 0) {
            sendFinishedUnmountBroadcast();
            }
            }
            
            for (int i = 0; i < mStorageVolumePreferenceCategories.length; i++) {
                StorageVolumePreferenceCategory svpc = mStorageVolumePreferenceCategories[i];
                if (path.equals(svpc.getStorageVolume().getPath())) {
                    svpc.onStorageStateChanged();
                    break;
                }
            }
        }
    };
 private void sendFinishedUnmountBroadcast() {
getActivity().sendBroadcast(new Intent(FINISHED_UNMOUNT));
}
    
private final BroadcastReceiver killBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (switched) {
switched = false;
killVoldAndSendMessage();
}
}
};
private void killVoldAndSendMessage() {
Process process;
try {
process = Runtime.getRuntime().exec("ps");
String pid = getVoldPid(process);
if (process.waitFor() == 0) {
INetworkManagementService managementService = getNetworkManagementService();
managementService.killVold(pid);




Message msg = new Message();
handler.sendMessageDelayed(msg, 10000);
}
} catch (IOException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
   
    // 通过执行ps命令,用正则把vold的pid找出来
private String getVoldPid(Process process) {
String pid = null;
try {
InputStream ins = process.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(ins));
String line = "";
StringBuilder sb = new StringBuilder(line);
while ((line = br.readLine()) != null) {
if (line.contains("/system/bin/vold")) {
sb.append(line);
}
}
String psContent = sb.toString();
Pattern p = Pattern.compile("\\d+");
Matcher m = p.matcher(psContent);
if (m.find()) {
pid = m.group();
}
} catch (IOException e) {
e.printStackTrace();
}
return pid;
}


3. netd是如何kill vold的呢
为NetworkManagementService增加killVold(String pid)接口
NetworkManagementService.java
public void killVold(String pid) {
mConnector.doCommand("bandwidth killvold " + pid);
}


/system/netd/commandListener.cpp
  int CommandListener::BandwidthControlCmd::runCommand(SocketClient *cli, int argc, char **argv) {
    if (argc < 2) {
        sendGenericSyntaxError(cli, "<cmds> <args...>");
        return 0;
    }


    LOGV("bwctrlcmd: argc=%d %s %s ...", argc, argv[0], argv[1]);


    // argv[2]为上面传进来的pid,组成字符串,通过system(cmd)执行kill vold
    if (!strcmp(argv[1], "killvold")) {
char cmd[20] = "kill ";
char *ptr;
ptr = cmd + strlen(cmd);
*ptr = 0;
char *pid = argv[2];
strcpy(ptr, pid);
SLOGI(" cmd========== (%s)", cmd);
system(cmd);
sendGenericOkFail(cli, 1);
return 0;
    }
   ...


  }



这样,就能在UI中进行动态切换了。









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值