上一篇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 ++;
}
} 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中进行动态切换了。
这次,是真正的内外卡切换。
大概思路:
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中进行动态切换了。