Android "无法查看***。请释放部分手机内存"

 

Android   "无法查看***。请释放部分手机内存"

(北大众志小本(MPRC)上移植Android 2.1

通过浏览器下载(ucweb)软件后,点击安装,弹出对话框:“无法查看***。请释放部分手机内存”。

通过logcat查看发现是:"Couldn't clear application caches"

跟进源码发现错误信息是从:PackageManagerService.java (frameworks/base/services/java/com/android/server) 中的freeStorage()函数中打印出来的。

 

retCode = mInstaller.freeCache(freeStorageSize); if (retCode < 0) Log.w(TAG, "Couldn't clear application caches"); 

 

通过一步步跟进

freeCache()->execute()->transaction()->connect()

connect()函数中实例化了一个本地的socket,用于进程间通信。那么它到底和谁在进行进程间通信呢?

 private boolean connect() { if (mSocket != null) { return true; } Log.i(TAG, "connecting..."); //第一次建立和服务器端的连接 try { mSocket = new LocalSocket(); LocalSocketAddress address = new LocalSocketAddress( "installd", LocalSocketAddress.Namespace.RESERVED); mSocket.connect(address); mIn = mSocket.getInputStream(); mOut = mSocket.getOutputStream(); } catch (IOException ex) { disconnect(); return false; } return true; } 

 

原来PackageManagerService在与init.rc中开启的守护进程:installd通信。

 

梳理一下我们可以推断错误来源:

客户端service发送的cmd"freeCache"没有得到服务器端守护进程的响应。

通过查看installd的相关源码installd.c中的

struct cmdinfo cmds[] = { { "ping", 0, do_ping }, { "install", 3, do_install }, { "dexopt", 3, do_dexopt }, { "movedex", 2, do_move_dex }, { "rmdex", 1, do_rm_dex }, { "remove", 1, do_remove }, { "freecache", 1, do_free_cache }, { "rmcache", 1, do_rm_cache }, { "protect", 2, do_protect }, { "getsize", 3, do_get_size }, { "rmuserdata", 1, do_rm_user_data }, }; 

我们可以找到响应命令的处理函数:do_free_cache

再一步步跟进:

do_free_cache()->free_cache()->disk_free()

下面就重点看看这个函数:

static int disk_free(void) { struct statfs sfs; if (statfs(PKG_DIR_PREFIX, &sfs) == 0) { return sfs.f_bavail * sfs.f_bsize; } else { return -1; } } 

可以发现这个函数首先通过statfs获取PKG_DIR_PREFIX("data/data")文件系统信息,然后返回非超级用户可以获取的空闲内存块数。

然后再看看保存文件系统信息的结构体

struct statfs { long f_type; long f_bsize;//每个内存块大小 long f_blocks; long f_bfree; long f_bavail;//非超级用户可以获取的空闲内存块数 long f_files; long f_ffree; __kernel_fsid_t f_fsid; long f_namelen; long f_frsize; long f_spare[5]; }; 

最后通过加调试信息,找到了错误的根源是google 源码本身的bug:

首先,statfs()函数获取"data/data"的信息成功,然后返回非超级用户可以获取的空闲内存块大小,即:sfs.f_bavail * sfs.f_bsize;

变量sfs.f_bavailsfs.f_bsize是两个long类型的变量,相乘后却保存在一个int类型的变量中,导致32位变量溢出越界,最终返回一个负数。free_cache()函数返回错误,最终客户端PackageManagerService收到错误信息反馈,导致安装失败。

另一方面,我们的众志小本硬盘空间比一般的娱乐终端(手机,pad)内存空间大很多,所以sfs.f_bavail的值会很大,相乘后的结果保存在int类型变量中,肯定会溢出。

解决方法:将disk_free()函数的返回值改为uint64_t就ok了。

补充

有空的话,仔细梳理一下应用程序的安装过程:

·通过adb install 命令安装

·通过软件安装

·installd 和PackageInstall.apk和PackageManagerService.java之间通信

 

你可能感兴趣的:(Android "无法查看***。请释放部分手机内存")