您的位置:首页 > 运维架构

昨天遇到个dexopt失败的奇怪问题,记录一下

2012-07-13 11:23 369 查看
昨天写个应用,push到系统中,不能被识别安装了应用,重启后桌面会出现图标,但是点击就会运行失败,log如下

E/dalvikvm( 2114): Bogus handler offset: 0x4

E/dalvikvm( 2114): Trouble with item 108 @ offset 0x3f9f4

E/dalvikvm( 2114): Swap of section type 2001 failed

E/dalvikvm( 2114): ERROR: Byte swap + verify failed

D/PowerManagerService(290): onSensorChanged: light value: 38, mHighestLightSensorValue:-1

D/PowerManagerService(290): lcdValue == mLightSensorScreenBrightnessWait, do nothing

E/LiZhao( 1322): **** getExternalStorageDirectory value = 0

E/dalvikvm( 2114): Optimization failed

W/installd(120): DexInv: --- END '/system/app/SuperPowerSave.apk' --- status=0xff00, process failed

E/installd(120): dexopt failed on '/data/dalvik-cache/system@app@SuperPowerSave.apk@classes.dex' res = 65280

D/PowerManagerService(290): setKeyboardVisibility: false

V/ActivityManager(290): ACT-Launching: ActivityRecord{42b6b788 com.cly.SuperPowerSave/.SuperPowerSaveActivity}

D/NetworkPolicy(290): onRecv: MSG_FOREGROUND_ACTIVITIES_CHANGED pid:uid:act=2100:1000:true

D/ActivityManager(290): ACT-AM_RESTART_ACTIVITY ActivityRecord{42b6b788 com.cly.SuperPowerSave/.SuperPowerSaveActivity}Task:11

I/PackageManager(290): Running dexopt on: com.cly.SuperPowerSave

D/dalvikvm( 2115): threadid=1: interp stack at 0x433cd000

D/PowerManagerService(290): onSensorChanged: light value: 46, mHighestLightSensorValue:-1

D/PowerManagerService(290): lcdValue == mLightSensorScreenBrightnessWait, do nothing

E/dalvikvm( 2115): Bogus handler offset: 0x4

E/dalvikvm( 2115): Trouble with item 108 @ offset 0x3f9f4

E/dalvikvm( 2115): Swap of section type 2001 failed

E/dalvikvm( 2115): ERROR: Byte swap + verify failed

D/PowerManagerService(290): onSensorChanged: light value: 52, mHighestLightSensorValue:-1

D/PowerManagerService(290): lcdValue == mLightSensorScreenBrightnessWait, do nothing

E/dalvikvm( 2115): Optimization failed

W/installd(120): DexInv: --- END '/system/app/SuperPowerSave.apk' --- status=0xff00, process failed

E/installd(120): dexopt failed on '/data/dalvik-cache/system@app@SuperPowerSave.apk@classes.dex' res = 65280

V/WindowManager(290): Changing focus from Window{41967ae0 com.cly.launcher2/com.cly.launcher2.Launcher paused=false}to null

D/color(461): ----------------------------- res = 0

I/WindowManager(290): Losing focus: Window{41967ae0 com.cly.launcher2/com.cly.launcher2.Launcher paused=false}

V/ActivityManager(290): getTasks: max=1, flags=0, receiver=null

V/ActivityManager(290): com.cly.SuperPowerSave/.SuperPowerSaveActivity: task=TaskRecord{42b6bd70 #11 A com.cly.SuperPowerSave}

V/ActivityManager(290): We have pending thumbnails: null

I/SurfaceFlinger(113): [SurfaceFlinger] frames:25, duration:1.516000, fps:16.485920

I/SurfaceTexture(113): [com.cly.launcher2/com.cly.launcher2.Launcher(0x3c3168)] disconnect: api=2

D/dalvikvm( 2100): DexOpt: --- BEGIN 'SuperPowerSave.apk' (bootstrap=0) ---

D/dalvikvm( 2116): threadid=1: interp stack at 0x4311f000

E/dalvikvm( 2116): Bogus handler offset: 0x4

E/dalvikvm( 2116): Trouble with item 108 @ offset 0x3f9f4

E/dalvikvm( 2116): Swap of section type 2001 failed

E/dalvikvm( 2116): ERROR: Byte swap + verify failed

最好搞了半天,回退代码,发现一个函数很长导致了这个问题,将这个函数屏蔽掉,没有问题;后面分成两个函数些,有问题;再屏蔽其中一个又没问题;最后分成三个函数写,就没有问题了。觉得非常奇怪,记录下来,后续参考。如果有走过路过的大神知道原因,也请不吝赐教!在此先行谢过。

自己在网上找了两篇文章,这里摘录一下:

第一篇,原文:http://www.open-open.com/lib/view/open1327556525374.html

android应用程序管理主要由PackageManager这个类来管理,实现PackageManager这个抽象类的是ContextImpl.java。在ContextImpl.java中,有一个内部静态类叫ApplicationPackageManager,实现了所有PackageManager的接口。

1
static
final
class
ApplicationPackageManager
extends
PackageManager
{
2
3
..........
4
5
ApplicationPackageManager又是通过对IPackageManager封装调用,来实现的。

01
public
PackageInfo
getPackageInfo(String packageName,
int
flags)
02
throws
NameNotFoundException
{
03
try
{
04
PackageInfo
pi = mPM.getPackageInfo(packageName, flags);
05
if
(pi
!=
null
)
{
06
 
return
pi;
07
}
08
}
catch
(RemoteException
e) {
09
throw
new
RuntimeException(
"Package
manager has died"
,
e);
10
}
11
12
throw
new
NameNotFoundException(packageName);
13
}
这里的mPM就是IPackageManager,PackageManagerService就是对IPackageManager的实现。所以我们平时对PackageManager的调用,最终是的在PackageManagerService.java中实现的。在PackageManagerService.java每个方法的实现。

PackageManagerService启动流程:

PackageManagerService(context, factoryTest)是包管理服务的主进程。它完成了对/system/app,/data/app,/system/framework,/data/app-private下的apk文件的解析。详细流程如下:

1.建立java层的installer与c层的installd的socket联接,使得在上层的install,remove,dexopt等功能最终由installd在底层实现

2.建立PackageHandler消息循环,用于处理外部的apk安装请求消息,如adb install,packageinstaller安装apk时会发送消息

3.解析/system/etc/permission下xml文件(framework/base/data/etc/),包括platform.xml和系统支持的各种硬件模块的feature.主要工作:

(1)建立底层user ids和group ids 同上层permissions之间的映射;可以指定一个权限与几个组ID对应。当一个APK被授予这个权限时,它也同时属于这几个组。

(2)给一些底层用户分配权限,如给shell授予各种permission权限;把一个权限赋予一个UID,当进程使用这个UID运行时,就具备了这个权限。

(3) library,系统增加的一些应用需要link的扩展jar库;

(4) feature,系统每增加一个硬件,都要添加相应的feature.将解析结果放入mSystemPermissions,mSharedLibraries,mSettings.mPermissions,mAvailableFeatures等几个集合中供系统查询和权限配置使用。

4.检查/data/system/packages.xml是否存在,这个文件是在解析apk时由

writeLP()创建的,里面记录了系统的permissions,以及每个apk的name,codePath,flags,version,uesrid等信息,这些信息主要通过apk的

AndroidManifest.xml解析获取,解析完apk后将更新信息写入这个文件并保

存到flash,下次开机直接从里面读取相关信息添加到内存相关列表中。当有apk

升级,安装或删除时会更新这个文件。

5.检查BootClassPath,mSharedLibraries及/system/framework下的jar

是否需要dexopt,需要的则通过dexopt进行优化

6.启动AppDirObserver线程监测/system/framework,/system/app,/data/app,/data/

app-private目录的事件,主要监听add和remove事件。对于目录监听底层通过

inotify机制实现,inotify 是一种文件系统的变化通知机制,如文件增加、删除

等事件可以立刻让用户态得知,它为用户态监视文件系统的变化提供了强大的支持。

当有add event时调用scanPackageLI(File , int , int)处理;

当有remove event时调用removePackageLI()处理;

7.对于以上几个目录下的apk逐个解析,主要是解析每个apk的AndroidMa-

nifest.xml文件,处理asset/res等资源文件,建立起每个apk的配置结构信息,

并将每个apk的配置信息添加到全局列表进行管理。f

8.将解析的每个apk的信息保存到packages.xml和packages.list文件里,

packages.list记录了如下数据:pkgName,userId,debugFlag,dataPath(包的数据路径)

dexopt:
如果我们想要求运行时的性能有进一步提高,就仍然需要对DEX文件进行进一步优化。优化DEX会产生一个可以快速载入执行的classes.dex文件,会进行包括byte-swapping,structure
realigning与basic structure checks,更新ODEX header ,为了确保产生ODEX流程的正确性,Android提供了一个dexopt工具,用来做为虚拟机的辅助工具,可以在系統启动时,透過Dalvik虚拟机對載入的DEX文件執行佳化操作。

优化发生的时机有两个:

对于预置应用,可以在系统编译后,生成优化文件,以ODEX 结尾。这样在发布时除
APK 文件(不包含 DEX)以外,还有一个相应的 ODEX 文件;

对于非预置应用,包含在 APK 文件里的 DEX 文件会在运行时通过dexopt进行优化,优化后的文件将被保存在缓存中(data/dalvik-cache)。

android安全机制概述:

Android是一个权限分离的系统 ,这是利用Linux已有的权限管理机制,通过为每一个Application分配不同的uid和gid, 从而使得不同的Application之间的私有数据和访问(native以及java层通过这种sandbox机制)达到隔离的目的 。与此同时,Android还在此基础上进行扩展,提供了permission机制,它主要是用来对Application可以执行的某些具体操作进行权限细分和访问控制,同时提供了per-URI permission机制,用来提供对某些特定的数据块进行访问。

Application 级别通过user ID和group Id实现安全控制;component级别通过permission来限制对于某一组件的访问;在data级别通过基于permission的per URI进行安全控制。

uidgidgids:
Android 的权限分离的基础是建立在 Linux 已有的 uid 、 gid 、 gids 基础上的 。

UID :Android 在 安装一个应用程序,就会为 它 分配一个 uid 。其中普通 Android 应用程序的 uid 是从 10000 开始分配 (Process.FIRST_APPLICATION_UID
), 10000 以下是系统进程的 uid 。

GID :对 于普通应用程序来说, gid 等于 uid 。由于每个应用程序的 uid 和 gid 都不相同, 因此不管是 native 层还是 java 层都能够达到保护私有数据的作用


GIDS : gids 是由框架在 Application 安装过程中生成,与 Application 申请的具体权限相关。 如果 Application 申请的相应的 permission
被 granted ,而且中有对应的 gids , 那么这个Application 的 gids 中将包含这个gids 。

installer类:
构造方法中,首先会进行一些成员变量的初始化,比如mContext, mFactoryTest, mMetrics, mSettings等。

最重要的是初始化mInstaller这个变量:

1
Installer
installer =
new
Installer();
2
if
(installer.ping()
&& Process.supportsProcesses()) {
3
mInstaller
= installer;
4
}
else
{
5
mInstaller
=
null
;
6
}
Installer这个是PackageManager与底层C模块进行通信的工具类,同socket进行通信,PackageManager所有对apk的安装,卸载等操作都是通过Installer进行的。对Installer的调用首先会调用ping()来判断socket是否已经连接。

连接方法:connect:

01
private
boolean
connect()
{
02
if
(mSocket
!=
null
)
{
03
return
true
;
04
}
05
Slog.i(TAG,
"connecting..."
);
06
try
{
07
mSocket
=
new
LocalSocket();
08
09
LocalSocketAddress
address =
new
LocalSocketAddress(
10
"installd"
,
LocalSocketAddress.Namespace.RESERVED);
11
12
mSocket.connect(address);
13
14
mIn
= mSocket.getInputStream();
15
mOut
= mSocket.getOutputStream();
16
}
catch
(IOException
ex) {
17
disconnect();
18
return
false
;
19
}
20
return
true
;
21
}
transaction首先会判断连接,如果socket连接正常,就将cmd命令写入socket文件,并且接收返回信息,并且返回给execute:

01
private
synchronized
String
transaction(String cmd) {
02
 
if
(!connect())
{
03
 
Slog.e(TAG,
"connection
failed"
);
04
 
return
"-1"
;
05
 
}
06
07
 
if
(!writeCommand(cmd))
{
08
 
/*
If installd died and restarted in the background
09
*
(unlikely but possible) we'll fail on the next
10
*
write (this one).Try to reconnect and write
11
*
the command one more time before giving up.
12
*/
13
 
Slog.e(TAG,
"write
command failed? reconnect!"
);
14
 
if
(!connect()
|| !writeCommand(cmd)) {
15
 
return
"-1"
;
16
 
}
17
 
}
18
//
Slog.i(TAG,"send: '"+cmd+"'");
19
 
if
(readReply())
{
20
 
String
s =
new
String(buf,
0
,
buflen);
21
//
Slog.i(TAG,"recv: '"+s+"'");
22
 
return
s;
23
 
}
else
{
24
//
Slog.i(TAG,"fail");
25
 
return
"-1"
;
26
 
}
27
28
 
}
执行命令的方法:execute(String cmd):

1
private
int
execute(String
cmd) {
2
String
res = transaction(cmd);
3
try
{
4
return
Integer.parseInt(res);
5
}
catch
(NumberFormatException
ex) {
6
return
-
1
;
7
}
8
}
install()方法:

01
public
int
install(String
name,
int
uid,
int
gid)
{
02
 
StringBuilder
builder =
new
StringBuilder(
"install"
);
03
 
builder.append(
'
'
);
04
 
builder.append(name);
05
 
builder.append(
'
'
);
06
 
builder.append(uid);
07
 
builder.append(
'
'
);
08
 
builder.append(gid);
09
 
return
execute(builder.toString());
10
}
其他方法:

(1)dexopt(String apkPath, int uid,boolean isPublic);优化dex文件

(2)movedex(String srcPath, String dstPath); 移动dex文件

(3)rmdex(String codePath);删除dex文件

(4)remove(String name);移动apk

(5)rename(String oldname, String newname);重命名

(6)deleteCacheFiles(String name);删除cache文件

(7)clearUserData(String name);删除user data

(8)freeCache(long freeStorageSize);释放cache空间

(9)setForwardLockPerm(String packagePathSuffix, int gid) 为apk文件增加前缀

(10)getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath, PackageStats pStats)获取apk信息

(11) moveFiles();移动文件。

另一篇,将对odex文件的处理。

原文:http://www.haogongju.net/art/45064

Android的ROM中有很多odex文件,相对于APK中的dex文件而言这个odex有什么作用呢?
如果你仔细观察会发现文件名时一一对应的,同时那些对应的apk文件中没有dex文件。这样做可以使其厂商保证一定的反盗版,因为没有没有dex文件的apk是无法正常安装的,而厂商直接将odex和不完整的apk文件放到手机rom固化到/system/bin中可以让一般用户无法正常导出使用

可能想到的是合并odex和apk变成apk中包含dex文件的,这样合并后最终apk文件安装在/data/中,而rom存放在 /system/app中,所以最终导致了用户可装在Android手机中的软件会变少,占用系统空间

一、APK生成odex文件方法:

编译开源GIT上的build/tools/dexpreopt/dexopt-wrapper这个,使用dexopt-wrapper即可,操作步骤

将dexopt-wrapper放到/data/local目录中,使用adb shell执行linux命令行,使用cd命令进入/data/local目录,

cd /data/local

./dexopt-wrapper android123.apk android123.odex

二、合并odex和 少dex的apk 为完整的apk文件

1. 下载 http://smali.googlecode. href="http://www.haogongju.net/tag/com" target=_blank>com/files/smali-1.2.2.jar 和http://smali.googlecode.com/files/baksmali-1.2.2.jar,既然是需要盗版,还需要odex所在rom中的一些引用类,一般在
/system/framework 目录中

2 .通过odex生成class文件

java -jar baksmali-1.2.2.jar -x android123.odex

执行完上面这行命令后,会生成一个out 文件夹里面是android123.odex的class文件。出现问题,根据提示可以从rom的
/system/framework 中的xxx.jar放到pc上的java环境变量中一起生成。

3. 通过class生成classes.dex 文件。

java -Xmx512M -jar smali-1.2.2.jar out -o classes.dex

4. 将classes.dex放到apk文件

因为apk是zip的mime编码类型,使用winzip或winrar直接拖入到apk改名为zip的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息