【Android】多功能二维码实现思路,自动连接WI-FI
2015-09-25 17:40
781 查看
现在项目的需求是:
1. 带AP功能的机顶盒端能生成二维码,供手机客户端扫描
1.1 如果用非特定应用(手机助手)扫描,则跳转下载手机助手界面
1.2 如果用手机助手扫描,自动连接到该机顶盒的WI-FI
2. 不带AP功能的机顶盒也能生成二维码
2.1 同1.1
2.2 如果用手机助手扫描,自动连接到该机顶盒所连接到的WI-FI
现代应用中,二维码最常存的文本就是URL,所以也可以想象成二维码其实就是一个URL地址。所以扫描二维码可以跳转到某个界面。
比如我现在做的项目的二维码URL是“http://appproxy.topway.cn:8080/index.htm”,用手机端打开就能够跳转到下载页面,用电脑端打开却显示不出内容。这是因为服务器端会对访问端进行判断,看是否是移动设备,然后进行相应的操作(跳转下载界面也要区分iOS和Android端)。
学过web开发的都知道,网络请求有一种GET方式,是直接把参数放在URL后面的,比如下面扩展的URL:
http://appproxy.topway.cn:8080/index.htm?ssid=xxx&pwd=xxx
这个字符串就带了WI-FI名称和密码。像一些速食店现在都有一个二维码贴在桌子上的,目的就是让用户扫一扫然后自动连接上WI-FI了。但是前提是要用他们公司的应用扫才有用。这是因为URL后面带的参数大家定义的都不同,需要由协商好的软件去处理大家不同的需求,所以才会出现需要用专门的软件去扫一扫。
(同理,网上有的网站说你输入你家店的WI-FI和密码自动免费帮你生成二维码,让店家瞬间高大上,但是客人要连上你家WI-FI必须下它家的产品才可以,原理已经说过了,就是这样来扩展市场的)
好了,扫盲讲到这应该差不多了。
现在针对我的项目需求讲下思路。
那第一个问题最直接的就是:怎么生成二维码?
用到的是google的一个开源二维码项目——zxing,目前基本上和二维码打交道的东西,都会用到它。
只提思路,具体怎么实现另搜百度就好。
(PS:这里发现把二维码改成其他颜色扫描无效,只有黑色可以被应用扫描到,背景改为透明没有关系)
然后我这边建立了个Service,读取机顶盒的AP信息,包括SSID和密码,与访问应用地址形成最终的URL,再通过zxing生成二维码。
到这一步需求1.1已经完成了,因为其他应用扫描二维码会忽略到后面的参数,只识别前面的地址,就会跳转到下载界面。
为了实现1.2,我们在自己的应用扫描时做特别判断,也就是获取后面的参数值,都获取到WI-FI和密码了,就可以通过代码进行自动连接了!~
有两种方法,第一种通过系统API,在11年以后已经不能获得明文密码了,有密码全部用
记得在Manifest文件中添加许可
第二种用代码写命令去访问(《Android之查看Wifi密码》)
我已经找到
上面的方法我实现行不通,报错
这种要求有root权限,且应用还有访问权限,我在Manifest里加上
额外的想法:
又想过能不能直接用输入输出流访问
不过总觉得有隐患,另一个想法时,在连接WI-FI时,我们就额外保存一份密码到别处,然后供我们其他的应用访问,这样安全性好像又不好。
这个问题我还没有解决,若有人已经解决或者有解决思路,望留言告诉一下,提前谢过!~
2015.9.25 更新:已解决获取
前提:1.有源码环境 。2.作为系统应用。
经过四天苦战,终于搞定没有权限的问题了。我们的项目是在机顶盒端,而我的应用是作为“系统应用”存在的,而盒子又不会给应用开放 root 权限,但是我的应用能在源码编译成系统应用,是能够获取系统权限的,可是用上述代码还是访问不了
以下讲解决办法:
1.在Manifest中加入
这样做以后,就能够获取系统权限,但必须在源码环境中编译了(无法在eclipse中编译)。
2.把上述read代码
3.最重要的一点,做底层的发现我的应用已经有了system权限,但是还是读取不到wifi文件夹里的内容,于是他通过串口,发现
4.在源码环境中编译,并把编成的apk装在
5.在上面的前提下,发现直接用File读取文件(不能用*代替,要写具体文件名),也可以读到,代码如下:
Reference:
1.知乎 -《为何用二维码扫描App扫描微信名片都能直接跳转到微信?这是如何实现的?》
2.《Android之查看Wifi密码》
3.《Android-WIFI密码破解工具编写初探》
4.《Android平台利用ZXING生成二维码图片》
5.《提高zxing生成二维码的容错率及zxing生成二维码的边框设置》
1. 带AP功能的机顶盒端能生成二维码,供手机客户端扫描
1.1 如果用非特定应用(手机助手)扫描,则跳转下载手机助手界面
1.2 如果用手机助手扫描,自动连接到该机顶盒的WI-FI
2. 不带AP功能的机顶盒也能生成二维码
2.1 同1.1
2.2 如果用手机助手扫描,自动连接到该机顶盒所连接到的WI-FI
首先,必须了解什么是二维码?
简单来说,二维码就是把一段纯文本用图形样式转换出来了,以便于快速扫描读出。现代应用中,二维码最常存的文本就是URL,所以也可以想象成二维码其实就是一个URL地址。所以扫描二维码可以跳转到某个界面。
比如我现在做的项目的二维码URL是“http://appproxy.topway.cn:8080/index.htm”,用手机端打开就能够跳转到下载页面,用电脑端打开却显示不出内容。这是因为服务器端会对访问端进行判断,看是否是移动设备,然后进行相应的操作(跳转下载界面也要区分iOS和Android端)。
为什么用微信扫一扫能直接加关注某个人而不是跳转URL呢?
这是因为微信是一个”特别”的应用,扫的是”特别”的二维码。学过web开发的都知道,网络请求有一种GET方式,是直接把参数放在URL后面的,比如下面扩展的URL:
http://appproxy.topway.cn:8080/index.htm?ssid=xxx&pwd=xxx
这个字符串就带了WI-FI名称和密码。像一些速食店现在都有一个二维码贴在桌子上的,目的就是让用户扫一扫然后自动连接上WI-FI了。但是前提是要用他们公司的应用扫才有用。这是因为URL后面带的参数大家定义的都不同,需要由协商好的软件去处理大家不同的需求,所以才会出现需要用专门的软件去扫一扫。
(同理,网上有的网站说你输入你家店的WI-FI和密码自动免费帮你生成二维码,让店家瞬间高大上,但是客人要连上你家WI-FI必须下它家的产品才可以,原理已经说过了,就是这样来扩展市场的)
好了,扫盲讲到这应该差不多了。
现在针对我的项目需求讲下思路。
1. 带AP功能的机顶盒端能生成二维码,供手机客户端扫描
有必要再科普一下,AP就是Access Point,接入点的意思,就是说这个机顶盒能够自己发射WI-FI供别的设备接入。那第一个问题最直接的就是:怎么生成二维码?
用到的是google的一个开源二维码项目——zxing,目前基本上和二维码打交道的东西,都会用到它。
只提思路,具体怎么实现另搜百度就好。
(PS:这里发现把二维码改成其他颜色扫描无效,只有黑色可以被应用扫描到,背景改为透明没有关系)
然后我这边建立了个Service,读取机顶盒的AP信息,包括SSID和密码,与访问应用地址形成最终的URL,再通过zxing生成二维码。
到这一步需求1.1已经完成了,因为其他应用扫描二维码会忽略到后面的参数,只识别前面的地址,就会跳转到下载界面。
为了实现1.2,我们在自己的应用扫描时做特别判断,也就是获取后面的参数值,都获取到WI-FI和密码了,就可以通过代码进行自动连接了!~
2. 不带AP功能的机顶盒也能生成二维码
通过前面的分析,2.1不用改代码就可以实现,关键是2.2,如何能获得本机已经连接过的WI-FI的密码?有两种方法,第一种通过系统API,在11年以后已经不能获得明文密码了,有密码全部用
*代替值返回
WifiManager wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); List<WifiConfiguration> conList = wifiManager.getConfiguredNetworks(); for (WifiConfiguration wifiConfiguration : conList) { Log.d("wifi", "SSID = " + wifiConfiguration.SSID); Log.d("wifi", "psk = " + wifiConfiguration.preSharedKey); }
记得在Manifest文件中添加许可
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
第二种用代码写命令去访问(《Android之查看Wifi密码》)
我已经找到
data/misc/wifi/wpa_supplicant.conf里确实有明文密码。
public StringBuffer read() throws Exception { Process process = null; DataOutputStream dataOutputStream = null; DataInputStream dataInputStream = null; StringBuffer wifiConf = new StringBuffer(); try { process = Runtime.getRuntime().exec("su"); dataOutputStream = new DataOutputStream(process.getOutputStream()); dataInputStream = new DataInputStream(process.getInputStream()); dataOutputStream.writeBytes("cat /data/misc/wifi/*.conf\n"); dataOutputStream.writeBytes("exit\n"); dataOutputStream.flush(); InputStreamReader inputStreamReader = new InputStreamReader( dataInputStream, "UTF-8"); BufferedReader bufferedReader = new BufferedReader( inputStreamReader); String line = null; while ((line = bufferedReader.readLine()) != null) { wifiConf.append(line); } bufferedReader.close(); inputStreamReader.close(); process.waitFor(); } catch (Exception e) { throw e; } finally { try { if (dataOutputStream != null) { dataOutputStream.close(); } if (dataInputStream != null) { dataInputStream.close(); } process.destroy(); } catch (Exception e) { throw e; } } StringBuffer sb = new StringBuffer(); Pattern network = Pattern.compile("network=\\{([^\\}]+)\\}", Pattern.DOTALL); Matcher networkMatcher = network.matcher(wifiConf.toString()); while (networkMatcher.find()) { String networkBlock = networkMatcher.group(); Pattern ssid = Pattern.compile("ssid=\"([^\"]+)\""); Matcher ssidMatcher = ssid.matcher(networkBlock); if (ssidMatcher.find()) { sb.append(ssidMatcher.group(1)); Pattern psk = Pattern.compile("psk=\"([^\"]+)\""); Matcher pskMatcher = psk.matcher(networkBlock); if (pskMatcher.find()) { sb.append(pskMatcher.group(1)); } else { sb.append("无密码" + "/n"); } } } return sb; }
上面的方法我实现行不通,报错
java.io.IOException: write failed: EPIPE (Broken pipe),应该是权限不够。
这种要求有root权限,且应用还有访问权限,我在Manifest里加上
android:sharedUserId="android.uid.system",然后到源码里去编译。
额外的想法:
又想过能不能直接用输入输出流访问
data/misc/wifi/wpa_supplicant.conf。
不过总觉得有隐患,另一个想法时,在连接WI-FI时,我们就额外保存一份密码到别处,然后供我们其他的应用访问,这样安全性好像又不好。
这个问题我还没有解决,若有人已经解决或者有解决思路,望留言告诉一下,提前谢过!~
2015.9.25 更新:已解决获取data/misc/wifi/wpa_supplicant.conf
获取密码问题
前提:1.有源码环境 。2.作为系统应用。经过四天苦战,终于搞定没有权限的问题了。我们的项目是在机顶盒端,而我的应用是作为“系统应用”存在的,而盒子又不会给应用开放 root 权限,但是我的应用能在源码编译成系统应用,是能够获取系统权限的,可是用上述代码还是访问不了
data/misc/wifi/wpa_supplicant.conf文件内的内容。
以下讲解决办法:
1.在Manifest中加入
<manifest ... package="com.azz.wifipsk" ... coreApp="true" android:sharedUserId="android.uid.system" >
这样做以后,就能够获取系统权限,但必须在源码环境中编译了(无法在eclipse中编译)。
2.把上述read代码
Runtime.getRuntime().exec("su");改一下,已经有系统权限了,就不要执行
su了(不然会报
uid 1000 not allowed to su的错误)
class MyThread implements Runnable { public void run() { Process process; StringBuilder content = new StringBuilder(); String cmd = "cat /data/misc/wifi/wpa_supplicant.conf"; //(不能用*代替,要写具体文件名) try { process = Runtime.getRuntime().exec(cmd); //有系统权限后直接执行命令 DataOutputStream dataOutputStream = new DataOutputStream(process.getOutputStream()); DataInputStream dataIntputStream = new DataInputStream(process.getInputStream()); DataInputStream dataErrorStream = new DataInputStream(process.getErrorStream()); dataOutputStream.writeBytes(cmd + "\n"); dataOutputStream.flush(); Thread.sleep(2000); Log.d("wifi", "input = " + dataIntputStream.readLine()); Log.d("wifi", "error = " + dataErrorStream.readLine()); String line = ""; if (dataIntputStream.available() > 0) { String error = ""; int total = dataIntputStream.available(); Log.e("TotalCount", Integer.toString(total)); int i = 0; while(i < total) { line = dataIntputStream.readLine(); if(line.trim().startsWith("ssid=") || line.trim().startsWith("psk=")) { content.append(line + "\n"); } i += line.length() + 1; } dataOutputStream.close(); dataErrorStream.close(); dataErrorStream.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("Exception1", e.toString()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); Log.e("Exception2", e.toString()); } Message msg = new Message(); Bundle b = new Bundle();// 存放数据 b.putString("info", content.toString()); Log.e("info", content.toString()); msg.setData(b); MainActivity.this.myHandler.sendMessage(msg); // 向Handler发送消息,更新UI } }
3.最重要的一点,做底层的发现我的应用已经有了system权限,但是还是读取不到wifi文件夹里的内容,于是他通过串口,发现
/data/misc/wifi目录的权限是
wifi.wifi,即wifi组创建,且属于wifi组,那么只有wifi组成员才能够访问,通过命令
root@XXXX:/data/misc # chown system.wifi wifi/将wifi组的权限改为
system.wifi,然后“系统应用”就可以访问了。
4.在源码环境中编译,并把编成的apk装在
/system/app/目录下(好像我用adb装在
/data/目录下也可以)
5.在上面的前提下,发现直接用File读取文件(不能用*代替,要写具体文件名),也可以读到,代码如下:
String path = "/data/misc/wifi/wpa_supplicant.conf"; File file = new File(path); InputStream in = null; String line, content = ""; try { in = new FileInputStream(file); InputStreamReader inReader = new InputStreamReader(in); BufferedReader bufferedReader = new BufferedReader(inReader); while((line = bufferedReader.readLine()) != null) { content += line + "\n"; } Log.d("file", "path = " + path + ", content = " + content); } catch (Exception e) { Log.e("file", e.getMessage()); } finally { if(in != null) { in.close(); } }
Reference:
1.知乎 -《为何用二维码扫描App扫描微信名片都能直接跳转到微信?这是如何实现的?》
2.《Android之查看Wifi密码》
3.《Android-WIFI密码破解工具编写初探》
4.《Android平台利用ZXING生成二维码图片》
5.《提高zxing生成二维码的容错率及zxing生成二维码的边框设置》
相关文章推荐
- Android应用是否可以只有一个Service或Broadcast Reciver,而没有Activity?
- 【Android】 从头搭建视频播放器(3)——手势检测 & 控制
- Android -- Properties使用
- android模拟器中关闭应用进程
- 查看
- [转]Android自定义控件系列五:自定义绚丽水波纹效果
- 解决引用单个Library因android-support-v4.jar版本不同而导致的冲突
- Service 简单例子
- android 折线图之二MPAndroidChart
- Android开发:shape和selector和layer-list的(详细说明)
- android sdk update
- Android Studio
- Connectivity-----Wi-Fi Peer-to-Peer
- Android源代码单个模块编译
- android 系统重启关机 方法 非常好的一篇文章
- 【Android应用开发】Android Studio 简介 (Android Studio Overview)
- 【Android应用开发】Android Studio 简介 (Android Studio Overview)
- android画折线图之一AChartengine
- Android 百度自动更新(升级)SDK的使用
- Android xml资源文件中@、@android:type、@*、?、@+含义和区别