标签归档:Android

给 Android ROM(AOSP)集成 SuperSU 的方法

Published / by sickworm / 给 Android ROM(AOSP)集成 SuperSU 的方法有4条评论

本文实验配置:

ROM: AOSP Android-6.0.1_r77
设备:Nexus 5
SuperSU: SR5-SuperSU-v2.82-SR5-20171001224502.zip

SuperSU 官方下载链接:https://download.chainfire.eu/1220/SuperSU/

处理 SELinux

我们给 Android 编译 ROM 的时候,可以编译出带有 root 权限的版本。但是由于 Android 4.4 以上 SELinux 的存在,apk 还是无法获得 root 权限,这样很多 root app 都会提示无法获取 root,不能正常使用了。想要 apk 可获得 root 权限,有两种方法:

  1. 关闭 SELinux 监控
  2. 配置 SELinux 权限

由于方案 2 需要熟悉复杂的 selinux 权限模型,而且我在实验过程中按网上办法硬是配置不上 domain(提示我没有设置 domain。有可能是因为我没有清空输出物,但不确定),所以我最后使用了方案 1。办法是在 $YOUR_AOSP/device/$YOUR_RAND/$YOUR_DEVICE/BoardConfig.mk 中的 BOARD_KERNEL_CMDLINE := xxxx下方加上:

BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive

这样就可以使 SELinux 不阻止你的操作了,permissive 只会打日志不会做实际动作,disabled 是完全关闭。注意加上这个后会通过不了 Google 的 CTS 测试,如果你是厂家的话需要注意这一点,个人开发者不需要关心。

当然,启动完成后进入 shell,以 root 权限执行 setenforce 0 也是可以达到关闭 SELinux 监控的效果。但每次重启都要设置一遍,比较麻烦。

好了,关闭 SELinux 了。但此时你用 apk 尝试获取 root 权限,发现还是不行。因为 AOSP 的 su 是只能 shell 或者 root 用的。解决办法是:

  1. 注释掉 system/extras/su/su.c 中 main 的一行 “not allow” 的代码,这行代码会判断 uid。注释后重新 make 一次 su 就能正常使用了
  2. 使用非 AOSP 的 su,SuperSU 提供的 su 需要配合 SuperSU 其他文件,不能直接用。

然后我发现改了 su 还是不行。。我猜测 framework 层可能还有权限处理机制,那我再给 rom 集成 SuperSU 授权管理,这样总可以了把。当然你也可以每次刷 rom 后重新刷一遍第三方 recovery,再刷一遍 SuperSU。这个集成我搞了 2 天才完成,网上资料不多,而且没一个能用。。

这里提一点,如果不搞好 SELinux,集成了 SuperSU 也是没法用的。如果你不想使用方案 2,我提供几篇文章以供参考:

Compile Android 5.1.1 ROM with ROOT by SuperSU

这篇没有配置 init.rc 里面的 service 的 SELinux domain。反正我不行。

init 启动 Native Service 时出现Service xxxx needs a SELinux domain defined; please fix 警告的说明
[Android][L][SELinux]Define a SELinux domain for Service

这 2 篇教你如何配置 SELinux domain。我配置完了还是说我没配置:“init 启动 Native Service 时出现Service xxxx needs a SELinux domain defined; please fix”。上面我也提到了,可能我没 clean/clobber 才失败的,如果你尝试成功了,不妨也告诉我。

实现 SELinux | AOSP

这是 AOSP 官方对 SELinux 的介绍,在这个问题里我没看到更多的信息。

集成 SuperSU

上面这篇文章Compile Android 5.1.1 ROM with ROOT by SuperSU 也提到了集成 SuperSU 的步骤。可惜日月变迁,现在 SuperSU 已经不是单一个 su 运作了。通过翻看 SuperSU 的指南 How-To SU,我得到几个信息:

  1. 最少需要这 4 个文件,就可以保证 SuperSU 正常运行:
    /system/xbin/su
    /system/xbin/daemonsu
    /system/xbin/supolicy
    /system/lib(64)/libsupol.so
    
  2. 需要在开机时执行 daemonsu --auto-daemon,启动守护进程。

  3. 执行 daemonsu --auto-daemon 时会调用 supolicy ,然后调用 libsupol.so。用于配置 SuperSU 的 SELinux 权限。

通过翻看 SuperSU 的刷 recovery 的包里的 update-binary.sh 我得到几个信息:

  1. daemonsu 和 su 是同一个文件

  2. 开机执行 daemonsu --auto-daemon 的方法有好几种,如果有 /system/int/init.d/ 这个文件夹(我没有)就放一个脚本进去,不然就是劫持 /system/bin/app_process(64),或是给 /system/放一个 install-recovery.sh。原文 Furthermore, daemonsu --auto-daemon needs to be launched somehow on boot. This is generally done via install-recovery.sh, 99SuperSUDaemon, or hijacking app_process([32|64]).

最后这条信息告诉我们 SuperSU 不需要关闭 SELinux 也可以使用,也告诉我们集成 SELinux 而不关闭 SELinux 的办法。方法 1 由于我手机没有,所以不用;2 劫持 app_process 那个是把 app_process 链接给了 su,具体我没看懂;3 install-recovery.sh 这个文件是刷 recovery 的时候会产生的,当系统启动的时候就会执行到它,刷完就会删掉这个文件。SuperSU 利用了这个特性,在 install-recovery.sh 里执行了daemonsu --auto-daemon,而且这个文件一直存在。因为执行 install-recovery.sh 对应的是 flash_recovery 的 SELinux domain,是已经配置好的。SuperSU 通过这三种方法来绕过 SELinux,然后再通过 sepolicy 来修改 SELinux 配置。

而我这边因为一直都是配置在 init.rc 里,且关闭了 SELinux,所以没有用到上面提到的方案。

集成步骤(在 Compile Android 5.1.1 ROM with ROOT by SuperSU 基础上修改)

  1. 删除原 su。文件位置 system\extras\su,最简单的方法是把该目录下的 Android.mk 重命名或者删掉。

  2. 添加 SuperSU 相关文件。包括上面提到的 4个 Linux 文件,和一个 SuperSU.apk。Linux 文件我选择在 packages 下新建了一个 su,放入上面那4个文件,以及创建 Android.mk。我比较懒,就没有做架构选择,就只把自己的 Nexus 5 对应的 armv7 包里的文件拷过来了,suinit 和 sukernel 不需要,复制一份 su 改名为 daemonsu。Android.mk 内容如下:

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE_TAGS := optional
    LOCAL_MODULE := su
    LOCAL_SRC_FILES := $(LOCAL_MODULE)
    LOCAL_MODULE_CLASS := EXECUTABLES
    LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
    LOCAL_UNSTRIPPED_PATH := $(LOCAL_MODULE_PATH)
    include $(BUILD_PREBUILT)
    
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE_TAGS := optional
    LOCAL_MODULE := daemonsu
    LOCAL_SRC_FILES := $(LOCAL_MODULE)
    LOCAL_MODULE_CLASS := EXECUTABLES
    LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
    LOCAL_UNSTRIPPED_PATH := $(LOCAL_MODULE_PATH)
    include $(BUILD_PREBUILT)
    
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE_TAGS := optional
    LOCAL_MODULE := supolicy
    LOCAL_SRC_FILES := $(LOCAL_MODULE)
    LOCAL_MODULE_CLASS := EXECUTABLES
    LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
    LOCAL_UNSTRIPPED_PATH := $(LOCAL_MODULE_PATH)
    include $(BUILD_PREBUILT)
    
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE_TAGS := optional
    LOCAL_MODULE := libsupol.so
    LOCAL_MODULE_CLASS := SHARED_LIBRARIES
    LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)
    LOCAL_SRC_FILES := $(LOCAL_MODULE)
    OVERRIDE_BUILD_MODULE_PATH := $(TARGET_OUT_INTERMEDIATE_LIBRARIES)
    include $(BUILD_PREBUILT)
    

    apk 我放在了 packages/apps/SuperSU,同样的,放入 Superuser.apk(上面的官方刷机包里有),同时创建 Android.mk,内容如下:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    
    LOCAL_MODULE := SuperSU
    
    LOCAL_SRC_FILES := Superuser.apk
    LOCAL_MODULE_CLASS := APPS
    LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
    LOCAL_CERTIFICATE := PRESIGNED
    
    include $(BUILD_PREBUILT)
    
  3. 把第 2 步添加的 packages 添加到编译路径中。我只给 Nexus 5 设备添加了(这样不好,因为第一步把所有设备的 su 都删掉了)。修改 $YOUR_AOSP/device/$YOUR_RAND/$YOUR_DEVICE/device.mk,添加:
    PRODUCT_PACKAGES += \
         power.hammerhead
    
    +# SuperSU
    +#PRODUCT_PACKAGES += \
    +#    su \
    +#    daemonsu \
    +#    supolicy \
    +#    libsupol.so \
    +#    SuperSU
    
  4. 添加相关文件权限。修改 system/core/libcutils/fs_config.c,寻找 “system/xbin/su”,修改:
    -    { 04750, AID_ROOT,      AID_SHELL,     0, "system/xbin/su" },
    +    { 06755, AID_ROOT,      AID_ROOT,     0, "system/xbin/su" },
    +    { 07777, AID_ROOT,      AID_ROOT,     0, "system/xbin/daemonsu" },
    +    { 06755, AID_ROOT,      AID_ROOT,     0, "system/xbin/supolicy" },
    
  5. 在 system/core/rootdir/init.rc 中添加服务:
    service console /system/bin/sh
         class core
         console
         disabled
    -    user shell
    -    group shell log
    +    user root
    +    group root 
         seclabel u:r:shell:s0
    
    service installd /system/bin/installd
         class main
         socket installd stream 600 system system
    
    +# SuperSU
    +service daemonsu /system/xbin/daemonsu --auto-daemon &
    +    class main
    +    oneshot
    +
     service flash_recovery /system/bin/install-recovery.sh
         class main
         oneshot
    
  6. 编译,刷进去。自此 apk 可通过 SuperSU 获得 root 权限!

存在问题

由于对 AOSP 不熟悉,文件放置不太讲究,不能迎合多设备编译的需求。

还有一个小问题,就是点击 SuperSU 进入主界面时会提示有新 SU 可用,可是我已经下载最新版本了。我估计因为这是最小系统,SuperSU 发现有些文件缺失所以做出提示。

此时注意:不可以点击 INSTALL!因为点击 install SuperSU 会把 install-recovery.sh 加到文件系统里,而我本来又在 init.rc 里写过一次启动了,这会导致多次启动 daemonsu,结果是系统起不来(也可能是别的原因,但至少可以确定的是当我没有在 init.rc 配置启动 daemonsu,而是手动更新的时候,此时点击 INSTALL 系统是能正常启动的)。如果用到 SuperSU 提到的那三种办法,估计就不会多次启动了。因为平时也不需要打开 SuperSU,我就不深究下去了,毕竟编译花时间。

版权所有,转载请注明出处:
http://sickworm.com/?p=367

Android Studio让module library application并存的尝试

Published / by sickworm / Android Studio让module library application并存的尝试有4条评论

在做UAF的时候,我产生了一个需求:我希望我可以把demoUAF ClientUAF ASM分别打包成3个apk,又可以打包成1个apk。当分别打包的时候,UAF Client/UAF ASM是application;统一打包的时候,UAF Client/UAF ASM是library。我开始进行尝试。

方案1: 通过apply library 和 application plugin进行配置

初步想法

我希望在debug的时候,UAF Client/UAF ASM的build.gradle apply plugin: 'com.android.library',release的时候apply plugin: 'com.android.application'。我找到识别debug和release的方法:

var debug = gradle.getStartParameter().getTaskRequests().toString().contains("Debug")
if (debug) {
    apply plugin: 'com.android.library'
} else {
    apply plugin: 'com.android.application'
}

因为我需要在Generate Signed APK中可以选择UAF Client导出apk,所以需要默认是application。(gradle进行sync的时候会进行一次配置,此时debug变量恒为false)

因为library module无可以配置applicationId,所以还需要:

android {
    defaultConfig {
        if (!debug) {
            applicationId "com.*****"
        }
        // ......
    }
}

然后在demo中加入依赖:

debugCompile project(':asm')
debugCompile project(':client')

存在问题

如此配置导致的问题是gradle sync now失败。报错是“demo无法依赖一个application的module”。但不sync直接运行demo和Generate Signed APK都是可以的。一开始我以为是我debug判断条件有问题,使用println打印信息发现没有输出。

后来了解到gradle sync会遍历一遍gradle文件进行配置,再执行常规编译。在遍历过程中并没有**Debug这样的task进行判断,所以此时UAF Client/ASM是当作application来处理的。

初步想法改进

那为了可以sync成功,我只能把默认改为library了。

var debug = gradle.getStartParameter().getTaskRequests().toString().contains("Debug")
if (!debug) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

这样一来,我就无法使用Generate Signed APK导出UAF Client/UAF ASM了。但同时我发现了一个有趣的现象:在这个配置之下,我导出demo-release.apk的同时,UAF Client和UAF ASM的release.apk也一并导出了。我猜测是配置过程由于我判断条件的干扰导致的。我试着导出demo-debug.apk,UAF Client和UAF ASM还是导出release.apk,但大小和原来不一样了(原本debug版本是带日志的,会大一点)。生成物是正确的,但名字不对。

然后我把混合/独立的判断条件从debug/release改为flavor控制之后,出现了更多的问题。

结论

不可行。

方案2:新建一个module,引用原library/复用原library代码

方案2一听就是靠谱的。通过新建一个module,引用原library module,专门用于导出apk;或通过配置,复用library module的main文件夹:

android {
    sourceSets {
        main.setRoot("../asm/src/main")
    }
}

结论

可行。

思考

为什么我会没想到简单的方案2,而是在方案1中纠结良久,不可自拔呢?因为我不久之前在某个网站(貌似是简书)上看过一篇文章,它最后说明了如何调试多个apk,就是debug的时候把application的module改为library module,release的时候再独立打包。我只留了个印象,没有收藏这篇文章。而到了用到这个的时候我找不到原文章了,但我内心会一直认为该方案可行,且解决方法优雅。

我再想了想,其实合并多个apk对调试并没有什么优势,这样的编译时间反而更长,只让修改部分的apk重新安装还更快速。。

——————————

我再想了想,那篇文章好像讲的是多个独立模块调试时,可以拆开apk独立运行,不用一点修改就要重新打包整个工程,以提高速度。。不过有了 instant run 这个技巧也不是特别有用了。

版权所有,转载请注明出处:
http://sickworm.com/?p=313

Android工程使用org.apache.commons.codec(commons-codec)库,运行时提示Base64.encodeBase64URLSafeString“java.lang.NoSuchMethodError”解决方法

Published / by sickworm / Leave a Comment

最近工作是开发FIDO UAF项目。FIDO UAF是一个旨在提供身份验证通用方案,以代替繁杂密码记忆的一个方案,Google,阿里这些大头都是核心成员,感觉是用来未来代替密码的1号方案。可能是定义的太通用了,UAF目前应用面还不广。

UAF还有个兄弟叫U2F,是个更精简的方案。U2F是一个用硬件外设(token)代替密码的方案,有点像u盾。外国已经有相关设备,Google登录已经可以通过U2F设备进行验证。使用很方便:你在电脑插入一个U2F设备(初次需要绑定账户),再打开网页,Google就自动为你登录。腾讯也有做一个叫QKey的,很快就会面市了。U2F实用性比较强。

回到正题,我在开发过程中,同事找到了eBay的UAF实现开源代码。我稍微看了一下,看到他们的Base64编码是使用了Base64.encodeBase64URLSafeString这个方法。我试了一下,和普通的Base64编码不同的是把编码末尾的”=“号去掉了。这样的话Http的Get参数就不会出现问题。虽然UAF要求使用POST发送请求,但我看到大佬都这么做,我就跟着用吧。

这个方法在org.apache.commons.codec(commons-codec)中定义。我也下了这个库,放在我自己的项目中,并替换了原来的Base64实现。而稍后在我进行单元测试的时候出问题了。Android单元测试提示我找不到“Base64.encodeBase64URLSafeString”这个方法。

这就奇怪了,编译期没有报错,运行时报错?我一下子没想明白。我反编译commons-codec库,里面是有这个方法的。我在运行时用反射打印出来,没有这个方法。然后我用Java单元测试试了一下,Java单元测试通过。

那看来是Android运行环境的问题了。我上网找了一下,居然有和我一样问题的人:
java.lang.NoSuchMethodError: org.apache.commons.codec.binary.Base64.encodeBase64

里面写的很清楚,Android的framework引用了一个旧版的commons-codec(而这个旧版本没有这个方法)。这会导致后加载的同一名称的包无效(无法加载并覆盖)。所以我在运行时就找不到这个方法了。

解决方法

没办法。Java的类加载安全机制决定了我们没办法使用后加载的类。我想起在“码农翻身”公众号里有看过相关的文章。我找了一下,节选如下:

我感到前途未卜, 但也不能坐以待毙, 一定得多了解信息。
“大哥, 你叫什么名字” , 我看小个子还算和气。
“我就是大名鼎鼎的文件验证器了, 能管很多事”
“那刚才他为啥还得请示上级呢” , 我用眼神指了一下开车的ClassLoader
文件验证器的声音一下子就压低了:
“你不知道,说来话长, 我们之前出现过事故,有个黑客写了个类java.lang.String, 和我们老板手下有一个干活最卖力的员工名字一模一样,只是这个黑客类里边竟然有格式化硬盘的代码,我们的小兵Classloader 不明就里,就把 这个黑客类给先装载了,也执行了, 最后的结果,唉,很惨的… ”

“那后来怎么办?”
“后来我们老板就定下了规矩:他的骨干员工像String, ArrayList等只能由他自己的心腹去装载, 我听说老板的心腹都是分层级的,像传销一样, 每个都有上线, 最顶层的叫Bootstrap Classloader , 下一次级叫Extension Classloader, 现在开车的这位其实叫App Classloader,位于最底层, 咱这位Classloader 在装载一个类之前,一定要问一问这几位权利极高的大爷,请他们先装载,这几位爷装载不了,才由我们这些小兵来出马。“

其实只要先加载了就没办法再重新加载了。我们普通app环境做不到上述黑客的环境,上面的文章只是给大家加深记忆。

不过如果你的类是你自己用Classloader 加载的话,还是可以重新加载的,相关文章:
JAVA动态重新加载Class机制

有办法。其实你把里面的实现拷出来,换个包名也可以。但是类之间的互相依赖可能会有点多。

但我突然发现,

为什么eBay的代码却用了这个库呢????他们是不是没有实际跑过呢???【黑人问号】

总结

Android工程使用org.apache.commons.codec(commons-codec)库,运行时提示“java.lang.NoSuchMethodError”的原因是:Android内部已经加载过同名的旧类库,导致项目中引用的库无法加载,而旧库的Base64类又没有这个方法。

解决方法是:
1. 换一个库,使用Android自带的Base64方法,并加上Base64.URL_SAFE标记位。(最好是Base64.NO_WARP | Base64.NO_PADDING | Base64.URL_SAFE)
2. 把实现拷出来(依赖过多,不现实)

版权所有,转载请注明出处:
https://sickworm.com/?p=175

linux中运行zipalign/aapt提示:No such file or directory解决办法

Published / by sickworm / Leave a Comment

最近在用Docker+Jenkins做持续集成(CI),中间有个步骤需要调用zipalign对齐jar包,但我运行zipalign的时候却提示:
No such file or directory。

这就奇怪了,找不到这个二进制文件???百思不得其解。。此时我在Mac和Windows上都是可以运行对应平台的zipalign。

首先,
我怀疑是我下的adt有问题。于是我下了3个版本的adt,都报这个错,排除。

然后,
我怀疑我的Docker虚拟机有问题,我把zipalign扔上阿里云运行,报同样的错误。

接着,
我猜测可能zipalign依赖同个文件夹里面的其他运行文件或so库。于是我整个adt拷过去,问题依然存在。

再然后,
求助谷歌,翻到若干的解决方案:
Add the tools to your PATH, or use the full path to zipalign.
copy the Zipalign file from sdk/build-tools/android-4.4W folder to sdk/tools/
以上皆无法解决。

后来我搜索“Linux no such file or directory原因” 找到:
Linux执行可执行文件提示No such file or directory的解决方法
原来是库不匹配的原因。
再搜索“Linux zipalign no such file or directory” 找到:
sudo apt-get install libc6:i386 libstdc++6:i386 zlib1g:i386

先apt-get update,再安装上面的库,提示找不到。(后来了解到要用dpkg拉i386的软件仓库才可以下载:i386的库。其实64位软件库也有32位的库)
我试着碰运气,直接安装libc++6,173M。(依赖大量32位库。后期实验,只需要安装lib32stdc++6和libc6-i386即可)
安装完成后,提示另一个错误:
error while loading shared libraries: libz.so.1: cannot open shared object file: No such file or directory
继续google,找到:
sudo apt-get install lib32z1
安装,解决。

所以zipalign:No such file or directory的原因是找不到匹配的32位库。
提示这个错误可能是二进制文件错误处理信息不够完善。

版权所有,转载请注明出处:
http://sickworm.com/?p=197

【Android XML】Android XML 转 Java Code 系列之 style(3)

Published / by sickworm / Leave a Comment

最近一个月把代码重构了一遍, 感觉舒服多了, 但总体开发进度没有变化.. 今天聊聊把style属性转换成Java代码的办法

 

先说结论: 引用系统style是无法完美的实现的, 我们如果有写成Java代码的需求, 请尽量避免使用系统style. 自定义style没问题.

 

style是什么?

(参考链接) http://developer.android.com/guide/topics/resources/style-resource.html

” A style resource defines the format and look for a UI. A style can be applied to an individual View (from within a layout file) or to an entireActivity or application (from within the manifest file).”

更通俗的理解是, style其实是放置一组attribute的宏, 在控件中指定这个style, 将在xml解析时将style的一组属性应用到该控件中. 举个栗子:

这里有个style, 定义在res/value中:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="CustomText" parent="@style/Text">
        <item name="android:textSize">20sp</item>
        <item name="android:textColor">#008</item>
    </style>
</resources>

然后我们在layout中的某个控件引用了这个style:

<?xml version="1.0" encoding="utf-8"?>
<EditText style="@style/CustomText" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Hello, World!" />

 

实际上, 他是等于:

<?xml version="1.0" encoding="utf-8"?>
<EditText android:textSize="20sp" android:textColor="#008" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Hello, World!" />

有点宏替换的味道, 如果接触过html的同学可以很容易想到CSS这种东西, 两个想要做的基本是一样的.

需要指明的是: style的属性的优先级是最低的, 会被控件的其他相同属性覆盖掉. 这是理所当然的, 不然控件里的attribute要不服了(你都把我写出来了, 居然和我说我没用, 要被style那货覆盖掉?!)

 

让我们再回到这个被我们定义的style上, 会发现他还有一个叫parent的属性. 这里我们用面向对象的父类来理解, 就是子style会继承父类的属性. 同样的, 父类的属性要比子类属性优先级要低. (你都把我写出来了, 居然和我说我没用, 要被老爸的style覆盖掉?!)

 

style不是什么?

style不属于android Namespace中的属性 (不以android:开头), 没有对应的setStyle方法可以使用, 我的理解是它属于xml预加载的一种”机制”.

 

自定义style的翻译:

那么对于自定义的style, 翻译就很简单了.

对于一个style, 将其替换成style中的attribute集合, 并向上 (父类) 继续增加父类集合, 并注意优先级 (子类可以覆盖父类attribute).

 

系统style的翻译:

系统style的翻译主要存在两个问题:

1. 系统style是内置在系统内部的, 不同的系统style并不一样, 特别是深度定制化的如MIUI, Flyme等操作系统. 如果只是使用一些简单的比较标准的style, 如progressbar等, 可以通过SDK Manager下载最新Android版本的API, 然后在路径platformsandroid-20dataresvalues中找到标准的系统style, 或直接下载ASOP(路径framework/base/core/res/res/values/styles.xml. (我在项目中用到的是4.4的style文件, 5.0的sytle文件分成了多个, 而且里面的命名有点不规范)

有些同学会发现, 如progressbar里面的style: android:attr/progressBarStyleSmall, 是用android:attr索引的. android:attr其实是在theme中定义的属性, apk的theme在AndroidManifest.xml中定义. 在解析xml过程中, 遇到android:attr的时候, 他就会在apk指定的theme中找到相应的item. 这个item对应的值就是style的值, 如:

style=?android:attr/progressBarStyleSmall 对应 最基本的Theme中的 <item name="progressBarStyleSmall">@android:style/Widget.ProgressBar.Small</item>

然后你再去styles.xml中寻找Widget.ProgressBar.Small. (这里还有个要注意的是, 就是系统的style不会声明parent属性, 但是以点”.”做层级, 比如这里的Widget.ProgressBar.Small父类是Widget.ProgressBar)

这样带来的另一个问题, 深度定制的操作系统各种主题也会有改动, 这样造成更大的偏差.

 

2. 系统style用到的资源, 有些是我们从外部无法获取的. 还是举Widget.ProgressBar.Small的例子:

    <style name="Widget.ProgressBar.Small">
        <item name="android:indeterminateDrawable">@android:drawable/progress_small_white</item>
        <item name="android:minWidth">16dip</item>
        <item name="android:maxWidth">16dip</item>
        <item name="android:minHeight">16dip</item>
        <item name="android:maxHeight">16dip</item>
    </style>

他里面用到了@android:drawable/progress_small_white这个系统资源, 但是这个资源不是public的, 我们实现的时候没办法把这个资源调用出来. 有些同学会想到反射, 但反射也是不行的. 因为所有的系统资源在ASOP编译的时候会生成类似于R.java这样的资源索引文件, 这些文件用于我们使用系统资源的id来调用系统资源. 而非public是不会声明出来的, 也就是说你连id都无法知道, 也就无法获取对应资源了. 我曾大致阅读了一下xml的解析实现, 发现到最后都进入了native层, 在native层用c/c++实现, 这让我望code兴叹..

不过ProgressBar的各种样式应该可以通过api来设置出来, 这个我还没证实. 可以参考: [Android实例] Android 在Java代码中设置style属性–使用代码创建ProgressBar对象

对这个非public系统资源获取有经验的同学欢迎交流.

 

项目代码:

我在项目中实现了构建style元素, 使用style元素替换为attritutes, 以及style元素之间的关系, 包括对theme的android:attr寻找:

https://github.com/SickWorm/AndroidXMLToJava/blob/master/src/com/excelsecu/androidx2j/AX2JStyle.java

 

版权所有,转载请注明出处:

http://sickworm.com/?p=36

【Android XML】Android XML 转 Java Code 系列之 介绍(1)

Published / by sickworm / Leave a Comment

最近在公司做一个项目,需要把Android界面打包进jar包给客户使用。对绝大部分开发者来说,Android界面的布局以XML文件为主,并辅以少量Java代码进行动态调整。而打包进jar包的代码,意味着无法通过常规的getResources(),getString()等方法来快速的获取资源,因为这些资源都是在apk安装的时候初始化生成的。为了满足客户的需求,笔者开始在网上寻找各种解决方案。结果如下:

 

1.apk 主体包方案

实现方法:安装一个新的apk,新apk和主apk使用android:sharedUserId连接起来,这样主apk就可以获取新apk的所有res资源。需要注意的是,R.id.xxx的值为新apk中的R.java的值。因此需要获取到新apk的R.class,或保证新apk和主apk的R.java中的资源的值是完全一致的,否则会出现获取资源对不上号的情况。
结论:可以实现,但需要多安装一个apk。静默安装apk需要root权限,否则会弹出安装界面。还没有找到不需要安装apk就能获取资源的方法。
参考链接:http://blog.csdn.net/lg707415323/article/details/7709076

2.LayoutInflater 方案

实现方法:通过LayoutInflater类解析外部XML资源,并转换为所需布局。
分析:Android解释因为性能原因,LayoutInflater只支持解析已经预编译在apk包里的xml。所以没办法解析外部导入的xml布局资源。

3.Android XML 转 Java Code方案  (开源/自研)

在线的Android XmToJaval转换服务,免费,支持各种控件(http://www.xmltojava.com/page/controls/ ),支持还算丰富,但转换完还需要一些简单的手动编辑。不支持drawable和values目录下的资源转换。
结论:能减轻手动翻译的工作量,但不能完全解决需求。drawable和string需要自己手动翻译实现。
个人开源项目,第一个项目用php写的,代码量很少,只更新过一次,应该没法用。第二个项目是Androidy应用动态解析,刚刚建立起来的,支持的字段很少,应该也没法用。完成度都很低。在Google和Github上都没有找到成熟的方案。
结论:不可用

4.zip包方案

只能替换图片资源,没办法解析Xml布局。(桌面应用的换肤功能都基于zip或apk这两个方案)
结论:不可行

5.ClassLoader方案

把apk包用ClassLoader加载进来,并通过反射的方法获取Activity或其他组件的各种方法,从而获得资源。
分析:实践发现,反射调用Activity
总结论:方案1最方便,但客户不一定能接受;方案3比较稳妥,但是耗费人力大。

 

由于方案1需要安装apk,如果要静默安装还需要root权限。这种方案过于累赘,只能选择Android XML翻译原生代码的方法。于是就有了AndroidXMLToJava管理工具的诞生。

接下来几篇会分享该转换工具的一些解决过程。项目快完善的时候会放上博客,现在暂时不放出来。

 

 

版权所有,转载请注明出处:

http://sickworm.com/?p=32

Ubuntu Touch环境搭建

Published / by sickworm / Leave a Comment

最近搞了一下Nexus 5的MultiRom Manger,体验了一把Ubuntu Touch和Android L,总体感觉还不错,不过Android L的NFC驱动还有问题,Ubuntu Touch优化还不足,画面有点卡,而且无法关背光。于是萌生了参与Ubuntu Touch驱动开发的念头,也算是把工作当成一种兴趣吧。

Ubuntu Touch for Nexus 5是非官方的,官方的只有Nexus 4,7,10。我们从MultiRom Manager可以直接下载到for Nexus 5的最新版本。

鉴于中文博客中并没有相关教程,只能参照 Ubuntu Touch 环境搭建的官方文档:

https://wiki.ubuntu.com/Touch/Building

 

以下记录我搭建Ubuntu Touch的过程(跟着上面的wiki走)。

Building Ubuntu Touch Android pieces from source

Whether you want to build Ubuntu Touch for the currently supported Nexus devices or want to port it to a new target, you need to set up your working environment to build Android from source. This setup is more or less the same whether you are building AOSP or a project based on it such as CyanogenMod, SEAndroid or Ubuntu Touch.

If you are new to building Android sources you may want to check out this document and others in the Getting Started section on the Google documentation site, as it covers the basics and terminology of AOSP building. While Ubuntu Touch uses some helper scripts as detailed below, if will nonetheless be helpful to understand what is going on under the hood especially if you want to work on this part of the project.

http://source.android.com/source/initializing.html

For Ubuntu Touch, you can find all the needed Android git repositories at https://code-review.phablet.ubuntu.com/#/admin/projects/. This is basically a mirror of AOSP 4.4.2_r1, but containing only the needed low level services used by Android (e.g. no Dalvik at all).

For any Android related project on our git server, you’ll find a branch named phablet-4.4.2_r1. This branch contains a static known git HEAD and the required changes needed for Ubuntu, including our custom Android manifest.

从源码编译Ubuntu Touch的Android部分

无论你想编译现在已经支持的Nexus设备,还是想为一个新设备移植Ubuntu Touch,你都需要的搭建编译环境来编译Android源码。该教程大致和你编译AOSP(Android Open Source Project)或者基于AOSP的项目是一样的,如CyanogenMod,SeAndroid或者Ubuntu Touch。

如果你是第一次编译Android源码,你可能需要下面这个文档,和Google文档网站Getting Started章节上的其他相关文档,文档讲解了AOSP编译的基础和一些术语。在下面的环节中Ubuntu Touch会使用一些辅助脚本。如果你打算在项目上做相关开发,通过浏览这些文档你会更明白接下来的每一步是在做什么。

http://source.android.com/source/initializing.html

你可以在https://code-review.phablet.ubuntu.com/#/admin/projects/找到所有搭建Ubuntu Touch需要的Android git repositories(repo是一个多git项目管理工具,这个东西在天朝不稳定的网络中显得非常难用)。这是一个基于AOSP 4.4.2_r1的镜像,但只包含了低层Android运行所需要的服务(例如没有Dalvik)。

在我们的git服务器上任意一个Android相关的项目,你都会找到一个叫phablet-4.4.2_r1的分支,这个分支包含了一个git HEAD和包括custom Android manifest(Ubuntu Touch的repo的Manifest,包含了所有Ubuntu Touch的基础git项目)等对于Ubuntu需要修改的代码。(这段不太明白想说什么)

该段只是介绍,没有需要执行的部分。

Set up your development environment

At this moment we’re using the Android code base provided by Android AOSP.

Everything we take from Android is just C/C++, so you’ll notice that your Android build environment will be way smaller than when comparing to the traditional Android builds.

For development you can run any 64-bit Desktop version of Ubuntu between 12.04 LTS and 13.04.

It’s not strictly necessary, but it’s helpful to install ccache. (http://source.android.com/source/initializing.html#ccache in the general Android Setup guide should help with this.)

Additional packages which are used to build the host tools:

  • $ sudo apt-get install git gnupg flex bison gperf build-essential   zip bzr curl libc6-dev libncurses5-dev:i386 x11proto-core-dev   libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-glx:i386   libgl1-mesa-dev g++-multilib mingw32 tofrodos   python-markdown libxml2-utils xsltproc zlib1g-dev:i386 schedtool

On Utopic (and maybe other) the 4.8 version of g++ is needed:

  • $ sudo apt-get install g++-4.8-multilib

Before 14.04 Trusty you’ll also need to set up the tools PPA.

Then you need to install phablet-tools:

  • $ sudo apt-get install phablet-tools

This will also install the repo tool, used to sync the Android repositories. Learn more about the repo tool. If you already have a repo tool in your $PATH from previous android development, be sure to remove it or make sure it’s after the system repo command in the path. To check that the right repo command is used, you can issue $ which repo. This should return /usr/bin/repo.

You can check out the source code using the repo and git tools already familiar to Android ROM developers, as described here

https://wiki.ubuntu.com/Touch/AndroidDevel

Alternately, all the Android code can be downloaded using the phablet-dev-bootstrap tool provided by the phablet-tools package installed in the previous step. This tool is a Python wrapper around repo and used to also check out bzr repositories before all code was managed by repo and git. For the purposes of getting the source code for development it is no longer needed.

To get the code setup run:

  • phablet-dev-bootstrap [target_directory]

If for some reason the sync ends midway, you can continue the sync with the -c switch, so the command would be:

  • phablet-dev-bootstrap -c [target_directory]

Alternatively, if you are just building an image for an already supported device, you can specify the -v switch:

  • phablet-dev-bootstrap -v [device codenames] [target_directory]

The phablet-dev-bootstrap command will automatically use the repo tool with the Ubuntu Touch Preview custom manifest to download all the git repositories and needed data. Be aware that this step takes a long time and requires at least 15GB (plus 2-3GB for the binary output).

搭建你的编译环境

我们将使用Android基础组件(基于Android AOSP)来编译。

我们只会用到Android中的C/C++代码,所以你会发现你的编译环境会比传统的Android编译环境要小。

你可以使用Ubuntu 12.04LTS到13.04的64位桌面版来开发Ubuntu Touch。(博主使用Mint 14.04,基于Ubuntu 14.04)

我们可以安装ccache(ccache会加快Android编译速度),但不是必需的。(http://source.android.com/source/initializing.html#ccache Android配置向导会指导你如何安装)

我们还需要这些安装包来编译主机上的编译工具:

$ sudo apt-get install git gnupg flex bison gperf build-essential  zip bzr curl libc6-dev libncurses5-dev:i386 x11proto-core-dev  libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-glx:i386  libgl1-mesa-dev g++-multilib mingw32 tofrodos  python-markdown libxml2-utils xsltproc zlib1g-dev:i386 schedtool

在Utopic(Ubuntu 14.10)(或者其他)中,还需要安装g—4.8

$ sudo apt-get install g++-4.8-multilib

在Ubuntu 14.04稳定前你还需要安装PPA工具(只需要把“Add PPAs (pre Trusty only)”那一步执行就ok了。)

然后安装phablet-tools:

$ sudo apt-get install phablet-tools

这会安装repo工具,repo用于同步Android的repositories。 点击了解repo工具。如果你已经有了repo工具且已经在之前的Android开发中把执行路径加入到$PATH中了,请把repo删除,或保证在$PATH中repo的路径在系统的repo command的路径(如/bin/repo)之后。你可以使用$ which repo验证repo路径是否正确。如果正确,会返回/usr/bin/repo。

(这段还没弄清楚什么意思,因为我还没编译过,应该保证是/usr/bin/repo就可以了。到时候回来补充)

然后使用repo和git(只需要安装git,不需要实际执行,repo会调用git工具),从以下网址获取Android源码。这一步对于Android Rom开发者应该很熟悉。

https://wiki.ubuntu.com/Touch/AndroidDevel

(此链接会另写一篇文章,下载源码的工作都在这个链接中)

使用之前步骤已经安装了的phablet-dev-bootstrap,就可以一个个项目地下载所有的Android代码(大概可有640多个)。这个工具使用Python将repo封装了起来。在所有的代码被repo和git管理之前,这个工具也可以用于下载bzr repositories(和repo相似的一个管理工具)的工程。不过只用于获取开发用的源码的话,已经不需要这个功能了。

接下来让代码跑起来:

phablet-dev-bootstrap [target_directory]

如果发生某些错误导致sync中途停止了,你可以使用-c参数继续同步:

phablet-dev-bootstrap -c [target_directory]

如果你只是编译一个已经被支持的设备的烧录文件,你可以使用-v参数:

phablet-dev-bootstrap -v [device codenames] [target_directory]

phablet-dev-bootstrap命令会使用repo工具自动下载manifest(项目列表)然后下载所有的git项目和需要的数据。注意这个步骤要很长的时间,而且需要至少15GB(加上2-3GB的二进制文件输出)的空间。

 

版权所有,转载请注明出处:

http://sickworm.com/?p=21

Settings点击Location(位置)后右上角的开关button不会消失

Published / by sickworm / Leave a Comment

MT8121/8382平台:

 

前几天又遇到一个源码的bug。在10寸平板上,进入设置界面,点击Location(位置)项,右上角Title处会显示一个开关button,用来开关定位服务。但点完Location项再点其他项的时候,button并不会消失。直到你点到Developer(开发者选项),再点击其他项那个button才会消失。Developer项本身右上角也有一个开关button。

于是很自然地想到,在切换设置项时,Location页面在退出时没有把button去掉。

结果一看果然是。

 

--- a/packages/apps/Settings/src/com/android/settings/location/LocationSettings.java +++ b/packages/apps/Settings/src/com/android/settings/location/LocationSettings.java @@ -100,6 +100,9 @@ public class LocationSettings extends LocationSettingsBase super.onPause(); mValidListener = false; mSwitch.setOnCheckedChangeListener(null); +        getActivity().getActionBar().setCustomView(null);

 

版权所有,转载请注明出处:

http://sickworm.com/?p=19