在进行Brillo系统开发与学习的时候,有一个客户端程序可以很好地避免在命令行中输入繁琐的命令,进行设备的配对,获取access token会变得异常的简单。
- 使NdsManager发现局域网中的Brillo设备
Brillo设备上运行着avahi服务,这就意味着Android设备上的Nsd服务可以发现在同一个局域网中的Brillo设备。
相关的文档可以参考:
- http://developer.android.com/reference/android/net/nsd/NsdManager.html
- http://developer.android.com/training/connect-devices-wirelessly/nsd.html
相关的参考代码在:
development/sample/training/NsdChat
在Activity的onResume()中调用NdsManager.discoverServices(),去发现_privet._tcp服务;在onPause()中调用NdsManager.stopServiceDiscovery(); 当有相关的服务时,会回调NsdManager.DiscoveryListener.onServiceFound(), 这时候我们还需要通过NsdManager.resolveService()去获取ip地址(InetAddress)和端口号。当服务停止运行时,会回调NsdManager.DiscoveryListener.onServiceLost(),这时候也可以通过NsdManager.resolveService()去查找丢失服务的主机的ip地址,将列表中的那一项删除。
这时发现Brillo系统中的avahi service只发布了80端口的服务, secure http的端口号可以通过/privet/info查找。
- 建立https client访问设备
由于 Brillo系统中的web server使用的是自签名证书,所以在建立连接,进行访问的时候,要忽略证书。所以建立https连接会相对复杂一些。
使用HttpURLConnection来获取access token:
public static void deviceInfo(String path, OnMessage callback) { try { URL url = new URL(path + "/privet/info"); HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection(); urlConnection.setRequestProperty("Authorization", "Basic anonymous"); urlConnection.setRequestProperty("Content-Type", "application/json"); urlConnection.connect(); try { InputStream in = new BufferedInputStream(urlConnection.getInputStream()); BufferedReader bufReader = new BufferedReader(new InputStreamReader(in)); StringBuilder sb = new StringBuilder(); for (String line = bufReader.readLine(); line != null;) { sb.append(line).append("\n"); line = bufReader.readLine(); } callback.onMessage(sb.toString(), null); return; } finally { urlConnection.disconnect(); } } catch (MalformedURLException muex) { Log.e(TAG, "Malformed url: " + path); callback.onMessage(null, muex.getMessage()); } catch (Exception e) { e.printStackTrace(); callback.onMessage(null, e.getMessage()); } }
Android官网推荐使用HttpURLConnection来建立连接:
http://developer.android.com/about/versions/marshmallow/android-6.0-changes.html#behavior-apache-http-client
由于建立https连接需要处理服务器证书的问题,所以建立https连接还是使用apache http client:
private static class MySSLSocketFactory extends SSLSocketFactory { private SSLContext mSslContext; private Callback mCallback; public MySSLSocketFactory(KeyStore truststore, Callback callback) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { super(truststore); mCallback = callback; TrustManager tm = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chains, String authType) throws CertificateException { if (chains == null || mCallback == null) { return; } mCallback.onCertAvailable(chains, authType); } }; mSslContext = SSLContext.getInstance("TLS"); mSslContext.init(null, new TrustManager[] { tm }, null); } @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { Log.v(TAG, " -> Create socket: socket=" + socket + ", host=" + host + ", port=" + port + ", autoClose=" + autoClose); return mSslContext.getSocketFactory().createSocket(socket, host, port, autoClose); } @Override public Socket createSocket() throws IOException { return mSslContext.getSocketFactory().createSocket(); } } public static synchronized HttpClient getHttpClient(int port, int securePort, Callback callback) { try { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); SSLSocketFactory factory = new MySSLSocketFactory(trustStore, callback); factory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); HttpParams params = new BasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET); HttpProtocolParams.setUseExpectContinue(params, true); ConnManagerParams.setTimeout(params, 10000); HttpConnectionParams.setConnectionTimeout(params, 10000); HttpConnectionParams.setSoTimeout(params, 100000); SchemeRegistry reg = new SchemeRegistry(); reg.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), port)); reg.register(new Scheme("https", factory, securePort)); ClientConnectionManager connManager = new ThreadSafeClientConnManager(params, reg); return new DefaultHttpClient(connManager, params); } catch (Exception e) { e.printStackTrace(); } return new DefaultHttpClient(); }
相关的文档可以参考http://android-developers.blogspot.com/2011/09/androids-http-clients.html
- 功能与界面
可以看到在主界上NSD会一直偿试发现同一个局域网中的Brillo设备,并将找到的设备显示在下面的列表中。当然,如果Brillo设备不想广播自己的信息,你也可通过输入host name/ip address和端口号的形式来访问设备信息(当前只支持http):
在设备界面会显示当前所要访问的设备的IP地址和端口号。同时提供一组的菜单:
- INFO: /privet/info: 显示设备信息
- CERT:: 显示设备web server(https)使用的证书
- PAIRING START: /privet/v3/pairing/start:进行设备配对,以获取access token
- PAIRING CONFIRM: /privet/v3/pairing/confirm:确认配对
- PAIRING CANCEL: /privet/v3/pairing/cancel:取消配对
- AUTH: /privet/v3/auth:获取Owner的access token
在获取设备信息后,我们就可以知道http/https的端号了,之后的连接都是通过https访问:
系统证书信息:
开始进行配对的操作:
确认配对信息:
获取access token:
- 相关代码
代码已经上传到github.com上:https://github.com/brobwind/BroDm ->@<-, 可以通过git命令下载:
$ git clone https://github.com/brobwind/BroDm
由于有native 代码并且还需要使用到Brillo系统的代码,编译起来会相对复杂一些。编译使用的是在Android项目中编译,如果熟悉Android项目编译的话,会相对容易很多。
编译:
## Rebuild jni/libs/librokit.a $ mkdir -p /local/brillo-m9-dev && cd /local/brillo-m9-dev $ repo init -u https://android.googlesource.com/brillo/manifest -b brillo-m9-dev $ repo sync $ . build/envsetup.sh $ lunch brilloemulator_arm-eng $ mkdir -pv packages/apps && cd packages/apps $ git clone https://github.com/brobwind/BroDm.git $ cd BroDm/jni/libs && mma -j 8 After all the jni/libs/libbrokit.a will be updated ## Build BroDm APK $ mkdir -pv /local/android-5.1.1_r15 && cd /local/android-5.1.1_r15 $ repo init -u https://android.googlesource.com/platform/manifest -b android-5.1.1_r15 $ repo sync $ . build/envsetup.sh $ lunch aosp_arm-eng $ cd packages/apps $ git clone https://github.com/brobwind/BroDm.git $ cd BroDm && mma -j 8 After all the APK will be installed in: /loca/android-5.1.1_r15/out/target/product/generic/data/app/BroDm/BroDm.apk
- 通过WEB API执行相关的命令,如控制LED
传送门: ->@<-