在Aandroid系统启动后,Android系统会把APK的资源文件解压到系统的/data/app; 启动OOBE(开机设置程序);第一次启动与通常的启动是不同的。
在工厂生产系统时,为了测试产线的设备,必须需要开机测试。但是一但启动系统,产品到客户手中就不是第一次启动了。如果重新烧写系统,又比较耗费时间。为了解决这个矛盾,需要在生产线上,开机测试后清除第一次启动痕迹。
基本的方法如下:
1 在应用层,写一个小app,发出清除痕迹的命令。
2 在server层,添加一个ResetFactoryReceiver.java;接受应用层的“清除痕迹“的命令。
3 在recover层,添加功能reset-factory,处理“清除痕迹“的实现。具体就是删除/data/目录下面的所有内容,保留自己的备份文件夹。因为recover模式下,没有删除文件夹的功能函数,需要我们自己递归删除文件。
4 删除之后直接关机。具体代码如下:
1 的实现:发一个intent即可;
2 的实现;在/data/system/packages.xml文件,记录了文件启动的类名,只启动一次的程序OOBE,有属性disabled-components
删除disabled-components 属性,就可以让这个程序重新开机启动:
packages.xml文件省略内容如下:
/**
* ResetFactoryReceiver.java
* delete node
**/
publicclassResetFactoryReceiverextendsBroadcastReceiver {
privateString TAG ="ResetFactoryReceiver";
privateandroid.tv.TvManager mTM;
privatestaticfinalString xmlPath ="/data/system/packages.xml";
@Override
publicvoidonReceive(finalContext context, Intent intent) {
Log.i(TAG, TAG);
Context mCx = context ;
mTM = (TvManager) mCx.getSystemService(Context.TV_SERVICE);
rm_Pkgxml();
if(intent.getAction().equals("android.intent.action.resetfactory")) {
Thread thr = newThread("resetfactory") {
@Override
publicvoidrun() {
try{
Log.i(TAG, "resetFactory");
RecoverySystem.resetFactory(context);
} catch(IOException e) {
Slog.e(TAG, "Can't perform master resetfactory :", e);
}
}
};
thr.start();
}
}
privatevoidrm_Pkgxml() {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder db;
try{
db = factory.newDocumentBuilder();
Document doc = (Document) db.parse(newFile(xmlPath));
doc.normalize();
NodeList packagename = doc.getElementsByTagName("package");
inti =0;
for(; i
Element elt = (Element) packagename.item(i);
if(elt.getAttribute("name").equals("com.android.oobeshell")) {
NodeList list = elt.getChildNodes();
if(list.getLength() >0) {
for(intj =0; j
Slog.d(TAG,list.item(j).getNodeName());
// if("sigs".equals((list.item(j).getNodeName()))){
// list.item(j).getChildNodes().item(0).getAttributes().item(0).setNodeValue("1");
// }
if((list.item(j).getNodeName())
.equals("disabled-components")) {
elt.removeChild(list.item(j));
}
}
}
}
}
doc2XmlFile(doc,xmlPath);
} catch(ParserConfigurationException e) {
Slog.e(TAG,"ParserConfigurationException",e);
} catch(SAXException e) {
Slog.e(TAG,"SAXException",e);
} catch(IOException e) {
Slog.e(TAG,"IOException",e);
} catch(NullPointerException e){
Slog.e(TAG,"NullPointerException",e);
}
}
3,4 的实现:
/bootable/recovery/recovery.c
//需要保留的目录
staticchar*preserve_file[] = {
"/data/app",
"/data/res-private",
NULL
};
//bootable/recovery/recovery.c
在elseif(wipe_data) 之后
} elseif(wipe_data) {
printf("wipe_data..., default INSTALL_SUCCESS value = %d\n", status);
if(device_wipe_data()) status = INSTALL_ERROR;
if(erase_volume("/data")) status = INSTALL_ERROR;
if(wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
if(status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");
/* add by lhc 2011-12-16 */
} elseif(reset_data) {
printf("reset_factory..., default INSTALL_SUCCESS value = %d\n", status);
if(device_wipe_data()) status = INSTALL_ERROR;
if(erase_file("/data")) status = INSTALL_ERROR;
if(wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
if(status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");
/* add by lhc end */
//添加函数,递归删除文件
intremove_file(char*rmpath)
{
structstat statbuf;
structdirent *dirp;
DIR *dp;
intret = 0;
charchildpath[PATH_LEN] = {0};
char**preserve_p;
if(lstat(rmpath, &statbuf)
printf("%s can't lstat.\n", rmpath);
return-1;
}
for(preserve_p = preserve_file; *preserve_p; preserve_p++) {
if(!strcmp(rmpath, *preserve_p))
return0;
}
printf("rmpath = %s\n", rmpath);
if(S_ISDIR(statbuf.st_mode) == 0) {
unlink(rmpath);
printf("%s is not a dir, is removed.\n", rmpath);
return0;
}
if((dp = opendir(rmpath)) == NULL) {
printf("can't read dir: %s, %s\n", rmpath, strerror(errno));
return-1;
}
while((dirp = readdir(dp)) != NULL) {
if(strcmp(dirp->d_name,".") == 0 ||
strcmp(dirp->d_name, "..") == 0)
continue;
snprintf(childpath, PATH_LEN, "%s/%s", rmpath, dirp->d_name);
if((ret = remove_file(childpath)) != 0)/* R */
return-1;
}
if(closedir(dp)
printf("closedir error.\n");
return-1;
}
if(rmdir(rmpath) == -1) {
for(preserve_p = preserve_file; *preserve_p; preserve_p++) {
if(!strstr(*preserve_p, rmpath)) {
printf("rmdir %s error,%s.\n", rmpath, strerror(errno));
return-1;
}
}
}
printf("rmdir ok %s\n", rmpath);
returnret;
}
//递归删除文件
staticint
erase_file(constchar*path) {
ui_set_background(BACKGROUND_ICON_INSTALLING);
ui_show_indeterminate_progress();
ui_print("erase %s...\n", path);
if(!strcmp(path,"/data")) {
if(ensure_path_mounted(path) != 0) {
LOGE("ensure_path_mounted failed to nmount \"%s\"\n", path);
return-1;
}
returnremove_file("/data");
}
return0;
}
/* add by lhc 2011-12-16 */
if(update_package) {
set_recovery_status(status, "update");
} else{
set_recovery_status(status, "recovery");
}
/* add by lhc end */
//
printf("before finish_recovery.\n");
finish_recovery(send_intent);
printf("status = %d.\n", status);
if(status == INSTALL_SUCCESS) {
/* add by lhc 2011-12-16 */
//power off直接关机。
if(reset_data) {
printf("Power off...\n");
android_reboot(ANDROID_RB_POWEROFF, 0, 0);
}
/* add by lhc end */
printf("Rebooting...\n");
android_reboot(ANDROID_RB_RESTART, 0, 0);
}
//}
returnEXIT_SUCCESS;