首页 > 编程技术 > android

Android 5.0 SDK无法打开附带工具解决方法

发布时间:2016-9-20 19:58

最新更新了Android 5.0 的 SDK ,发现附带的工具比如uiautomatorviewer时提示“‘-v’ 不是内部或外部命令,也不是可运行的程序”,杯具了,幸好可以有google,问题终于解决。

作为日常工作,常常要使用Android SDK附带的hierarchyviewer、uiautomatorviewer、ddms等工具,刚好碰上Android L发布,并且更新了Android Stutio 1.0,SDK也伴随着进行了大幅度更新,包括uiautomatorviewer等在内的工具都有了新版本。于是昨天下了新版本过来,安装上去,接着重新设置了环境变量等一系列东西,然后打开uiautomatorviewer,竟然提示出错:

    ‘-v’ 不是内部或外部命令,也不是可运行的程序
    或批处理文件。
    无效路径
    ERROR: SWT folder ” does not exist.
    Please set ANDROID_SWT to point to the folder containing swt.jar for your platform.

莫非是我环境配置有问题?Google了一下,发现大部分答案都是让设置ANDROID_SWT的环境变量,或者吧jdk移动到PATH的第一个云云的奇怪办法,所以试了半天放弃了,还是安心调试调试看看咋回事。

把uiautomatorviewer.bat的第一行@echo off加冒号”:”注释掉,然后运新一下,看输出:

C:Usersxxx.xxx>rem Copyright (C) 2012 The Android Open Source Project ...... C:Usersxxx.xxxAppDataLocalAndroidsdktools>rem Check we have a valid Java.exe in the path. C:Usersxxx.xxxAppDataLocalAndroidsdktools>set java_exe= C:Usersxxx.xxxAppDataLocalAndroidsdktools>call libfind_java.bat SWT folder '' does not exist. Please set ANDROID_SWT to point to the folder containing swt.jar for your platform.

C:Usersxxx.xxx>rem Copyright (C) 2012 The Android Open Source Project
......
C:Usersxxx.xxxAppDataLocalAndroidsdktools>rem Check we have a valid Java.exe in the path.
C:Usersxxx.xxxAppDataLocalAndroidsdktools>set java_exe=
C:Usersxxx.xxxAppDataLocalAndroidsdktools>call libfind_java.bat
SWT folder '' does not exist.
Please set ANDROID_SWT to point to the folder containing swt.jar for your platform.
 

运行到call libfind_java.bat这一行出问题鸟~~

接着摸出来find_java.bat,注释掉第一行的 @echo off,看输出:

...... for /F "delims=" %a in ('"C:Usersxxx.xxxAppDataLocalAndroidsdktoolslibfind_java32 .exe" -s') do set java_exe=%a ......

......
for /F "delims=" %a in ('"C:Usersxxx.xxxAppDataLocalAndroidsdktoolslibfind_java32 .exe" -s') do set java_exe=%a
......
 

find_java32 .exe是什么鬼……

打开find_java.bat的代码,可以看到27行这里

for /f "delims=" %%a in ('"%~dps0find_java%arch_ext%.exe" -s -w') do set javaw_exe=%%a

for /f "delims=" %%a in ('"%~dps0find_java%arch_ext%.exe" -s -w') do set javaw_exe=%%a
 

就是本尊了,往上翻到arch_ext:

find /i "x86" > NUL && set arch_ext=32 || set arch_ext=64

find /i "x86" > NUL && set arch_ext=32 || set arch_ext=64
 

那个空格应该是这里了,可能是工程师们java写多了,bat里手也瓢了,把前后的空格去掉试试~~

find /i "x86" > NUL && set arch_ext=32||set arch_ext=64

find /i "x86" > NUL && set arch_ext=32||set arch_ext=64
 

关掉cmd,重新打开,新版的uiautomatorviewer出现鸟~~接着测试一下其他需要用到call libfind_java.bat的工具(tools里的几乎都要用吧),都没问题了。

drozer是一款针对Android系统的安全测试框架,可以帮助Android app和设备变得更安全,其提供了很多Android平台下的渗透测试exploit供你使用和分享,本文我们来讲讲Drozer工具安装使用。

最近接到任务,让了解一下几款Android安全测试相关的软件,首先是Drozer。Drozer是一款综合的安全评估和攻击的android框架,据 产品介绍 里说,Drozer可以全面评估app的安全性,并帮助团队把app的安全风险保持在可控范围内。
使用方法

1、在 mwrinfosecurity 公司的这个网页上,提供了社区版本的下载(没错,还有收费的高级版),下载并安装之。并保证android的adb环境已经配置好,即cmd中输入adb devices不会报错。并在手机端安装下载包中的Agent.apk的包。

2、在PC端开启转发

adb forward tcp:31415 tcp:31415

adb forward tcp:31415 tcp:31415

3、在手机端打开安装好的apk程序。

4、在PC上开启Drozer console

drozer console connect

drozer console connect

这样,就进入了 dz> 的console界面,后面就参照说明书一步一步进行测试。
功能介绍

dz> run app.package.list -f example

dz> run app.package.list -f example
 

list命令用以列出所有包含“example”的手机中已安装package名称,记住目标应用的完整名称。

dz> run app.package.info -a com.example

dz> run app.package.info -a com.example
 
info命令用以通过完整名称,获取该package的详细信息,比如data路径、apk路径、声明的权限等等。

dz> run app.package.attacksurface com.example

dz> run app.package.attacksurface com.example

attacksurface即攻击面分析,分析Activity/Broadcast Receiver/Content Provider/Service的权限,即是否能被其他的的应用程序调用。会像下列示例一样列出数量:

    Attack Surface:
    3 activities exported
    0 broadcast receivers exported
    2 content providers exported
    2 services exported
    is debuggable

dz> run app.provider.info -a com.example

dz> run app.provider.info -a com.example
 

获取provider的信息,比如是否需要额外权限来读写app的数据库,等等。如果找到相关漏洞,就可以通过

dz> run scanner.provider.finduris -a com.example

dz> run scanner.provider.finduris -a com.example
 

来扫描一些可用的uri,比如userName、password、ID之类的,甚至可以修改值(如SQL注入什么的):

dz> run app.provider.query content://xxxxxx --preinjection "xxx"

dz> run app.provider.query content://xxxxxx --preinjection "xxx"
 

dz> run scanner.provider.injection -a com.example

dz> run scanner.provider.injection -a com.example

进行简单的SQL注入检查、文件遍历检查之类的。

其他:

service相关

查询service

dz> run app.service.info -a com.example

dz> run app.service.info -a com.example

shell.start

在设备上启动一个可交互的linux shell

tools.file.upload / tools.file.download

在android设备上上传/下载文件

tools.setup.busybox / tools.setup.minimalsu

往设备上安装busybox和minimalsu

其他功能

在收费版本中,Drozer还提供了攻击面的可视化界面,并在多设备支持、模拟传感器输入等方面进行了探索,但是介于收费功能,没有继续看下去。

总结

在测试app的安全性上,Drozer确实可以发挥一些作用,不过关键问题还是要了解Android app的整体结构和安全策略,才能游刃有余,而不是见招拆招,被动的很。

Android开发有时我们要将干突然类库打包成jar方便合作协同的人调用,现在我们来谈谈Android如何打包Jar的常见问题及解决方案。

在Android开发过程中,我们经常会有这种需求,需要将自己开发一个类库打包成jar包以供他人(其他人一般指开发者)调用,而不是直接打包apk文件供最终用户使用。在打包成jar后,你往往会自己先测试一下看jar包能不能直接使用,这时就会常常有如下的几个问题:

1) Error…..Found duplicate file for APK:res/drawable-xxx/xxx.xx

2) res.Resources$NotFoundException:XXX ID#0x7f090015

3) java.lang.NullPointerException

4) java.lang.NoClassDefFoundError: com.xxx.xxx.R$layout

几个问题常见的原因是因为资源文件在作怪。

在第一个问你中,资源文件被打包进jar,在目标使用project中,也有相同id的资源文件,引起了冲突,解决方案是别打包资源文件啦,打包进去也不用的,后面会提到。或许你提到可能改变src project(提供jar包的project)里面的资源id可以表面上解决这个问题。

不打包资源文件,但src project中又引用了res的资源文件,即便你把相应的layout的xml啊String.xml啊等copy到目标project中,也会造成如res.Resources$NotFoundException或者java.lang.NullPointerException,原因是在src中的一些activity调用xml中的资源时,控件id找不到,例如一个btn = findViewById(R.id.btnXXX);这个btn为空,就会NullPointerException.这个问题原因还是在stackoverflow上有人给出了合理的解释.

As you want to import the resources,Since Android makes R class automatically with resources files under /res folder, using R class as final static is impossible.in your source code which will be exported in jar file, DON’T USE R variable because it will be replaced with final static memory address in compile time. Instead of using R use method below……

src项目中导出src文件夹成jar,不包含资源文件,资源文件copy到要引用的的其他项目中,但src中不能通过R.id.XXX 获取xml中的布局等。因为打包成class的时候,final int 等死了(具体可以看看gen/xxx.xx.R.java),在新项目中是不对应的。解决方案就是用反射的方法.代码如下.
   
public static int getResourseIdByName(String packageName, String className, String name)
{
    Class r = null;
    int id = 0;
    try
    {
        r = Class.forName(packageName + ".R");

        Class[] classes = r.getClasses();
        Class desireClass = null;

        for (int i = 0; i < classes.length; i++)
        {
            if (classes[i].getName().split("$")[1].equals(className))
            {
                desireClass = classes[i];
                break;
            }
        }

        if (desireClass != null)
            id = desireClass.getField(name).getInt(desireClass);
    } catch (ClassNotFoundException e)
    {
        e.printStackTrace();
    } catch (IllegalArgumentException e)
    {
        e.printStackTrace();
    } catch (SecurityException e)
    {
        e.printStackTrace();
    } catch (IllegalAccessException e)
    {
        e.printStackTrace();
    } catch (NoSuchFieldException e)
    {
        e.printStackTrace();
    }

    return id;

}

然后利用int id = getResourceIdByName(context.getPackageName(), “layout”, “main”);获取layout文件夹下main.xml的配置layout,通过int id = getResourceIdByName(context.getPackageName(), “string”, “text1″);获取string.xml下key为text1的字符串,通过int id = getResourceIdByName(context.getPackageName(), “id”, “btn”)获得id为btn的控件等。这个代码利用反射运行时找的。
当然具体应用过程中你可以根据上面的这个方法重构一下去掉循环找配置而修改成单独的一些id(key),dimen(key),color(key)之类的方法.这里就不便(工作需要)将源码给出当然也没必要给出了.

网上说使用library方法加载资源文件可以解决,即将src project作为library,目标project引用src project.然后说这个方法解决了通过R.XX.XX获取资源的问题,但还有一个问题是不可能把这个源码发布出去让其他人使用。我试了试,在我的项目中貌似通过R.XX.XX获取的控件仍然为null,让人会报空指针异常。

但这个给我提供了一个思路解决了一个比较方便的提供SDK的方式。因为上面的提到的那种需要手动copy layout等xml文件.如果利用library的话可以避免copy这个操作。具体方案就是,同样src project打包成lib.jar,然后自己手动建一个library project作为中间project,这个project添加lib.jar并将相应的xml文件copy到此project.如下图中的nebula_sdk,然后再发布nebula_sdk这个项目给其他人用,这样既满足了功能需求同样也使代码不易泄漏(打包过程仍然可以使用一定的代码混淆技术).

新建一个类库项目,如命名nebula_sdk.



发布出去后,开发者需要将上面提到的nebula_sdk作为library引用进来。如下图.

clip_image006

因此得到了两种android下发布sdk方案:

生成jar,发布jar包同时提供layout xml等配置资源文件.这也是现在很多sdk发布的方式,比如一下广告的SDKdomob_android_sdk,统计分析的SDK等.

生成jar包,先将library project并将此library project发布出去.这个方法可以省了让开发者copy 资源文件这一个过程.

当然如果src project中只涉及图片之类的(R.drawable.xx)文件(不涉及通过R.ID.xxx得到布局等控件)的话也可以按照网上所说的方法把图片copy到assert目录下然后通过File IO的方式获取原本应该放在drawable下的资源文件.当然如果涉及布局等控件外,还有另外一个解决方案然后布局的话可以用硬coding的方式编码(不读取xml布局),不过这个应该比较繁琐吧。

在学Android开发时,自己开发了一个实现自动定时切换飞行模式的功能,这个功能作用应该不大,不过也是为了学习。

整个功能要实现的话思路很清晰,只要改变一下系统配置(System.AIRPLANE_MODE_ON),发送一个通知即可。但发现Android的权限设置没有以前那么开放了。从Android 4.2开始(SDK API 17),设备的这些属性是只读的,官网上说:

Some device settings defined by Settings.System are now read-only. If your app attempts to write changes to settings defined in Settings.System that have moved to Settings.Global, the write operation will silently fail when running on Android 4.2 and higher.

所以对于Android4.2及其以上版本的手机来说就悲剧了~

刚开始在代码里面试图去修改System.AIRPLANE_MODE_ON 的属性,UI上貌似没有什么反映,log看到说缺少android.permission.WRITE_SECURE_SETTINGS 权限,在manifest里面声明,eclipse 又编译不过提示 Permission is only System Apps,说是必须得是系统应用才行。网上也有讨论各种解决方案,有说要写成系统应用,需要一个跟固件一起打包编译或者跟固件有相同的签名才行。同样也有人说通过反射可以实现。第一种方式应该很麻烦,得下某源码包编啊之类的,第二种方案有人说可以尝试下,但具体我也没试~不知道是否靠谱。

后来的后来,还是从网上找到了解决方案,不过device必须得root,不然没办法搞定。原文来自这里,虽然不知道是哪国语言~但代码还是认识。

//开启飞行模式
settings put global airplane_mode_on 1
am broadcast -a android.intent.action.AIRPLANE_MODE --ez state true
//关闭飞行模式
settings put global airplane_mode_on 0
am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false

通过adb shell,输入以上两句命令,可以将切换飞行模式。核心问题解决了~剩下的就是需要通过Java代码去调用shell命令,这个不难~不过得注意需要通过root去调用,另外得防止程序卡死~

再后来在stackoverflow上也有人提到了这个解决方案,并且还给了另外一个解决方案,即通过sqlite去直接改变Android的系统配置。数据库位置在/data/data/com.android.providers.settings/databases/settings.db

sqlite3 /data/data/com.android.providers.settings/databases/settings.db
insert into global values(null, 'airplane_mode_on', 1); //相应的插入0值,即是取消飞行模式

当然上面的数据库修改之后要生效,还是必须得发上面的那个broadcast才能生效。

下面是我写的小程序的界面:

android自动切换飞行模式

使用方法如下:

分别设定好需要开启、关闭飞行模式的时间,默认情况下为凌晨12点30分开启飞行模式,早上7点关闭飞行模式。然后打开当前状态的按钮即可,打开后会提示下次切换飞行模式的时间。

设定的时间到之后,App会自动尝试去启用飞行模式状态,如上图第二副图所示,当然得允许了(最好添加到信任程序列表,不然每次弹框)。另外,设定的时间到之后,有可能你还正在使用手机而不想马上切换飞行模式,App会给你5s的时间考虑,5秒之后你没有操作,app就会自动启用飞行模式了。当然你取消之后,这次就不会切换了,不过当你通过按手机返回键(Home键不会)或者杀掉这个进程再重新启动这个App的时候,会根据时间设定满足规则则自动切换。

切换”关闭”状态即取消设定功能,若当前手机正处于飞行模式状态,也会切换为正常状态。

注意:针对Android4.2及其以上的版本,手机需要ROOT才OK,且建议添加到信任程序列表。4.2之前的版本是木有问题的。

欢迎有兴趣的同学下载1 下载2试用,有建议/意见欢迎留言反馈~

TODO:

Android 各个版本UI风格的统一

增加多个时间段的设置,支持晚上睡觉和中午睡觉都转飞行模式,既省电又防打扰

自己写着瞎玩,应该有不少Bug

在Android开发中,插入图片可以直接调用拍照功能,但是在MIUI系统调用拍照后不返回当前的activity,那么如何解决呢?请看正文。

最近在做一个Android下的所见即所得的编辑器,思路是利用内置浏览器webview的html5属性contenteditable来实现,如:
<div id='con' class='con' contenteditable='true'>请输入内容</div>。

在实际应用过程中,由于需要插入图片,所以要支持利用摄像头拍照和从图库中的相册选择图片的功能。从图库选图没有任何问题,但是拍照的时候,在小米的miui系统下就出现了其他机型没有出现的bug。

我们先来看代码,先写调用camera的代码:

    camera_picname="camera.jpg"; //照片名称
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
    intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment.getExternalStorageDirectory()+defaultdir, camera_picname))); 
    startActivityForResult(intent, 10); 

注意,这里我们使用了startActivityForResult,表明需要返回结果。

然后需要重写当前activity的 onActivityResult 方法:

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == 10 && resultCode == Activity.RESULT_OK) {  /*拍照*/
     String imgpath = new File(Environment.getExternalStorageDirectory()+defaultdir, camera_picname).getAbsolutePath();
     try {
Thread.sleep(500); //延时毫秒 等待 WebView.loadUrl 异步执行完毕
} catch (InterruptedException e) {
e.printStackTrace();
}
     mWebView.loadUrl("javascript:window.appendcon('"+imgpath+"');");
    }
}

另外,为了防止在调用相机的时候,当前activity被系统kill(比如内存不够时,系统会自动销毁非可见的处于onPause或onStop状态的activity),我们需要 覆写 onSaveInstanceState方法,保存当前activity的状态变量值。

/*保存界面状态,如果activity意外被系统killed,返回时可以恢复状态值*/
@Override
public void onSaveInstanceState(Bundle savedInstanceState){
    savedInstanceState.putString("msg_con", htmlsource);
    savedInstanceState.putString("msg_camera_picname", camera_picname);
    super.onSaveInstanceState(savedInstanceState); //实现父类方法 放在最后 防止拍照后无法返回当前activity
}

然后在 onCreate(Bundle savedInstanceState) 方法里,判断savedInstanceState是否为null,不是null则读取上次保存的临时变量值。

上面的整个流程在模拟器里和其他型号的android真机设备里都没有问题,唯独红米的手机有问题!出现这个问题的手机型号是红米1S电信版,MIUI版本是: MIUI-JHCCNBF37.0,集成的Android版本是4.3 JLS36C;而这种情况在模拟器里或其他手机上没有发生。

具体问题:

点击拍照按钮,跳转到拍照界面时,由于拍照程序是比较消耗系统资源的,一般这个时候,系统大多会调用onSaveInstanceState方法让app保存界面状态值。

小米miui系统大多数时候也是正常调用 onSaveInstanceState 方法,当系统调用这个方法的时候,整个拍照流程,包括拍照后返回调用拍照的这个activity时,都是正常的。

但是,有些时候小米手机并没有调用 onSaveInstanceState 方法,这个时候,拍照后直接退出了调用他的这个activity(该activity覆写了onActivityResult方法),也就不会执行 onActivityResult方法。程序没有任何其他异常。

飘易的猜测是:由于红米miui深度定制了android系统,并且红米是整个小米系里最低端的入门机,在硬件性能上缩减严重。大白话就是修改了过多的模块而又没有考虑周全产生的bug。当系统调用拍照程序时,系统认为当前的内存还够用,activity不会被销毁,所以没有执行 onSaveInstanceState方法。但是拍照程序真正启用后,系统发现内存不够用了,即将OOM,就销毁了处于onPause或onStop的activity。这个销毁系统不会通知你,系统偷偷地干了这件事。

 正常的情况下,即使系统销毁了这个activity,在拍照返回到这个activity的时候,系统会重新生成被销毁的这个activity,重走 onCreate 等方法。

然而小米的miui系统在这里就有了bug!小米miui认为某个activity不会销毁,所以直接忽略了由于意外,系统需要重新创建这个activity的可能性。因此直接导致了返回到了前前activity,而不是前activity。

当小米miui系统明确地认为这个activity可能会被销毁时,执行 onSaveInstanceState 方法的时候,上面的这个bug不存在,系统在拍照完成后可以正常返回到前面的activity里并执行里面的 onActivityResult 方法。

目前,此bug没修复。上面的是飘易个人浅见,欢迎高手评论。

顺带说一下,红米手机的另外的bug:

一、有的时候在用手机浏览器访问网页或返回的时候,整个页面没有显示完全,网页上的部分元素显示不全、断层,必须手动刷新当前页,才能显示全部页面。

二、在打开APP时,点击按钮返回时,比如拍照返回时,界面上的有些控件是黑色的,必须手指滑过这些控件,才能去掉黑色。

不知道其他小伙伴们,有没有遇到如上bug。

标签:[!--infotagslink--]

您可能感兴趣的文章: