Bluetooth SPP with Android


Android has had Bluetooth (BT) Serial Port Profile (SPP) server and client capability since API Level 5 (version 2). Two Android devices, one acting as a server and the other as client, can communicate over BT SPP.

bt-spp-server

Bluetooth SPP Server Terminal app allows you to simulate a BT SPP peripheral. I used it recently to try and simulate a Push-To-Talk accessory for an app called Zello, running on another Android device.

Bluetooth Terminal is an open source app that can be used to create a BT SPP client connection with other devices, and exchange text and binary data.

Advertisements

HTTP/S capture using mitmproxy


This post shows how to install mitmproxy on Mac OS X (El Capitan) to capture HTTP/S traffic, especially useful when debugging applications.

I’ve been using Telerik Fiddler on Windows for sniffing HTTP/S and WebSocket traffic, but it isn’t very reliable on Mac or Linux. mitmproxy fills the lacuna well, but it does not yet support WebSocket traffic.

Install

Use pip to install mitmproxy thus

pip install mitmproxy

I encountered several compilation issues while installing through pip. I’ll go through them one by one. The first error results from failure to compile cryptography

    building '_openssl' extension
    clang -fno-strict-aliasing -fno-common -dynamic -I/usr/local/include -I/usr/local/opt/sqlite/include -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c build/temp.macosx-10.10-x86_64-2.7/_openssl.c -o build/temp.macosx-10.10-x86_64-2.7/build/temp.macosx-10.10-x86_64-2.7/_openssl.o
    build/temp.macosx-10.10-x86_64-2.7/_openssl.c:431:10: fatal error: 'openssl/aes.h' file not found
    #include <openssl/aes.h>
             ^
    1 error generated.
    error: command 'clang' failed with exit status 1

That can be resolved by executing pip to install cryptography thus

env LDFLAGS="-L$(brew --prefix openssl)/lib" CFLAGS="-I$(brew --prefix openssl)/include" pip install mitmproxy

With that dependency resolved, mitmproxy install fails with the following error

    building 'lxml.etree' extension
    clang -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -I/usr/local/opt/openssl/include -I/usr/include/libxml2 -Isrc/lxml/includes -I/usr/local/Cellar/python/2.7.9/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c src/lxml/lxml.etree.c -o build/temp.macosx-10.10-x86_64-2.7/src/lxml/lxml.etree.o -w -flat_namespace
    In file included from src/lxml/lxml.etree.c:323:
    src/lxml/includes/etree_defs.h:14:10: fatal error: 'libxml/xmlversion.h' file not found
    #include "libxml/xmlversion.h"
             ^
    1 error generated.
    Compile failed: command 'clang' failed with exit status 1
    cc -I/usr/include/libxml2 -I/usr/include/libxml2 -c /var/folders/3v/zgzrr9h96_34db7lt9_fx1wr0000gn/T/xmlXPathInitdIvQjA.c -o var/folders/3v/zgzrr9h96_34db7lt9_fx1wr0000gn/T/xmlXPathInitdIvQjA.o
    /var/folders/3v/zgzrr9h96_34db7lt9_fx1wr0000gn/T/xmlXPathInitdIvQjA.c:1:10: fatal error: 'libxml/xpath.h' file not found
    #include "libxml/xpath.h"
             ^
    1 error generated.
    *********************************************************************************
    Could not find function xmlCheckVersion in library libxml2. Is libxml2 installed?
    Perhaps try: xcode-select --install
    *********************************************************************************

Luckily, that error also shows the solution, run

xcode-select --install

Now, mitmproxy should install successfully.

Run

To capture HTTP/S traffic using mitmproxy traffic, run

mitmproxy

mitmproxy should show which port it is listening at; 8080 is the default. Use http://localhost:8080 as the HTTP proxy setting in browsers and applications.

Android emulator

This is how you can execute Android emulator to use mitmproxy as an HTTP proxy

export DYLD_FALLBACK_LIBRARY_PATH=~/Library/Android/sdk/tools/lib64
~/Library/Android/sdk/tools/emulator64-x86 -avd Nexus_S_API_21_x86 -http-proxy http://localhost:8080

The first line is needed so that the emulator can find the necessary libraries such as OpenGLES emulation library.

Pinned Certificates

If you try to access any site in the Android browser, or run any application that uses HTTP/S, mitmproxy will capture all traffic. To capture SSL traffic mitmproxy presents its own certificate to the applications. The root certificate that mitmproxy uses will need to be added to the certificate store, to avoid failures in certificate chain validation. This can be done by navigating to the special mitm.it URL in the browser, and picking your platform from the resulting page.

If you use certificate pinning in your applications, you can add ~/.mitmproxy/mitmproxy-ca-cert.cer to the list of certificates.

WebSocket traffic

mitmproxy does not support WebSocket traffic so connection establishment will fail. You can however setup mitmproxy to ignore traffic to a certain host:port. This can be leveraged to ask it to ignore WebSocket traffic.

mitmproxy --ignore 192\.168\.1\.10:888[1-9]

Tackling OAuth 2.0 in an Android app


This post shows how to perform OAuth 2.0 authorization in an Android app using WebView. It is meant for those who need to tackle OAuth 2.0 themselves, probably because their identity provider does not provide an Android library.

OAuth 2.0 defines a two-step process for obtaining an access token from an authorization server (aka identity service), that can subsequently be used to obtain resources from resource servers that trust the authorization server.

  1. Obtain authorization code from authorization server. This step is usually carried out once, within the browser, to mitigate the need for user credentials to be handled by clients. The user authenticates with the identity service, and authorizes requested scopes. The authorization service grants an authorization code as a result, and redirects the browser to a redirect URI specified by the client.
  2. Client uses the authorization code obtained through previous step, and performs a token request to authorization server with its own credentials such as client_id and client_secret. This step usually happens in a server application, but it’s done here on device. Some OAuth 2.0 providers will enable a simpler implicit grant flow, where step 1 above returns an access token, dispensing the need for step 2.

Here’s one way to carry out Step 1 in a WebView

// define REDIRECT_URI
final WebView webView = (WebView)findViewById(R.id.webView);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
WebViewClient webViewClient = new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith(REDIRECT_URI)) {
            Pattern p = Pattern.compile(".+code=(.+?(?=&|$))");
            Matcher m = p.matcher(url);
            if (m.matches()) {
                acquireAccessToken(m.group(1));
                // update UI
            }
            return true; // we've handled the url
        } else {
            return false;
        }
    }
};
webView.setWebViewClient(webViewClient);
// Prepare loginURL
webView.loadUrl(loginURL);

I intercept browser navigation using shouldOverrideUrlLoading. Upon detecting the redirect URI, I look for the authorization code, extract it using a simple regular expression, and initiate the procedure to obtain the access token, as described in step 2. It can be carried out using a simple REST request such as

    private void acquireAccessToken(String code) {
        // prepare url

        AsyncTask task = new AsyncTask<Object, Integer, String>() {
            @Override
            protected String doInBackground(Object[] urls) {
                return executeRequest((String) urls[0], "POST", "");
            }

            @Override
            protected void onPostExecute(String result) {
                try {
                    JSONObject json = new JSONObject(result);
                    String accessToken = (String)json.get("access_token");
                } catch(Exception ex) {
                    Log.e(TAG, "Request failed.", ex);
                }
            }
        };
        task.execute(url);
    }

To perform the HTTP POST request above, I use the HttpsURLConnection class, in the executeRequest convenience method, implemented as follows

    private String executeRequest(String url, String method, String content) {
        StringBuilder buffer = new StringBuilder();
        try {
            URL connUrl = new URL(url);

            HttpsURLConnection conn = (HttpsURLConnection)connUrl.openConnection();
            conn.setSSLSocketFactory(sslContext.getSocketFactory());

            if (content != null) {
                conn.setRequestMethod(method);
                conn.setRequestProperty("Content-Type", "application/json");
                conn.setRequestProperty("Content-Length", String.valueOf(content.length()));
                OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());
                for (int i = 0; i < content.length(); i++)
                    writer.write(content.charAt(i));
            }

            InputStreamReader reader = new InputStreamReader(conn.getInputStream());
            int c = reader.read();
            while (c != -1) {
                buffer.append((char)c);
                c = reader.read();
            }

            conn.disconnect();
        } catch (Exception ex) {
            Log.e(TAG, "Request failed.", ex);
        }
        return buffer.toString();
    }

Since my authorization server uses SSL certificates with custom CAs, I have need for a customized SSLContext that can perform SSL handshake using the custom CAs (certificate pinning). This is how sslContext above may be initialized

        // Create a KeyStore containing our trusted CAs,
        // see http://developer.android.com/training/articles/security-ssl.html
        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        Certificate ca = cf.generateCertificate(getResources().openRawResource(R.raw.cert_1));
        keyStore.setCertificateEntry("ca1", ca);
        ca = cf.generateCertificate(getResources().openRawResource(R.raw.cert_2));
        keyStore.setCertificateEntry("ca2", ca);

        // Create a TrustManager that trusts the CAs in our KeyStore
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        TrustManager[] trustManagers = tmf.getTrustManagers();
        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, trustManagers, null);

Certificates are packaged as raw resources under res folder. Android has special naming restrictions for raw resource file names; it only allows lower-case letters and underscores. Binary DER or corresponding textual PEM certificates work fine.

Moto E


Moto E

I just bought a Moto E for my daughter. At R$ 360, approximately $ 140, it is quite cost-effective. It’s in white, has digital TV, and comes with a couple of extra back covers. Here’s what I like about it in comparison to an LG L40 (D175F) I bought almost a year back

  • At 1 GB, it has twice the RAM of the L40
  • 5 MP back camera that, sadly, lacks autofocus
  • A 4.3 inches, 256 ppi (540 x 960 pixels) display

Common audio format between Android and iOS


If you need a common audio format between Android and iOS, try AAC codec with container format MP4. The file extension used is mp4.

On Android, record using encoding MediaRecorder.AudioEncoder.AAC and container format MediaRecorder.OutputFormat.MPEG_4. That should play all right on iOS 7, or better.

On iOS, record using the following settings

NSDictionary *recordSettings = 
    [NSDictionary
    dictionaryWithObjectsAndKeys:
    [NSNumber numberWithFloat:44100.0],AVSampleRateKey,
    [NSNumber numberWithInt: 2],AVNumberOfChannelsKey,
    [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
    [NSNumber numberWithBool:NO], AVLinearPCMIsBigEndianKey,
    [NSNumber numberWithInt:kAudioFormatMPEG4AAC], AVFormatIDKey,
    [NSNumber numberWithBool:NO], AVLinearPCMIsFloatKey,
    nil];

The resulting file should play all right on Android 4.3, or better.

What woes Samsung?


Samsung announced a rather lackluster quarter (compared to last year). What woes it? I am willing to hazard it is software. I was recently in the market for a cheap Android phone. Even though Samsung had an impressive lineup of hardware, none had Android 4.4 (KitKat) on it.

Low-end hardware requires Android KitKat due to improvements in how it supports low memory. The only phones that were touting Android 4.4 were from LG and Motorola. Motorola was still slightly costlier and Moto E hand’t arrived yet. Guess which I bought?

Monthly news review


This post reviews news in the month that has passed.

Everything Google announced at Google I/O 2014 in one handy list

Google announced a lot in one long keynote at I/O. Android One, Android Auto, Android Wear, and Android TV, are probably the big announcements this year. Looks like Google is prefixing all mobile-oriented hardware with Android, web-oriented hardware with Chrome, and services with Google. Unless it is something coming out of Nest, who’ve just launched a developer program for the programmable home.

Aereo Lost. What Now?

TV broadcasters are celebrating while the tech industry is up in arms. Are customers the real losers? Why should we need airwaves to transmit TV in the era of mobile internet? Why hasn’t the TV business adopted on-demand programming more actively? It isn’t as if their business isn’t being slowly driven to the ground.

This is Microsoft’s first Android smartphone, the Nokia X2

Nokia could have hedged its bets with Android a long while back. So why now? It is a cheap but attractive Android Smartphone full of Microsoft software and services.

Amazon’s Fire phone launch: Hits, misses, and takeaways

From inexpensive tablets to a fairly expensive phone, Amazon as come a long way. Lack of Bluetooth 4.0 (especially Smart) is annoying. It does seem to sport universal LTE, like the Moto G 4G. Will the Fire Phone truly delight users?

Google Donates Mod_Spdy To The Apache Foundation

HTTP 2.0 is around the corner and changes one crucial aspect of HTTP 1.x. It will no longer be a text-based protocol. An important feature is that data will be multiplexed over a single connection a browser maintains with a server.

Docker hopes its container platform will ease the lives of developers

Will an open container help big companies and other providers overcome the dependency on and momentum of Amazon, Google, and Microsoft?

Turing Test breakthrough as super-computer becomes first to convince us it’s human

Not everybody is convinced though, but the implications are important nevertheless. How do you know an e-mail message wasn’t sent by a real person? Are we at the cusp of having to deal with endless amount of believable spam? Looking at the positive aspects, customer support, distance education, and other areas that depend on personal interaction, may benefit.

Skype Translator Will Change the World

Real-time voice translation is a hard problem. You have to translate speech to text. The text then needs to be translated to the target language. The translated text then needs to be converted to speech. Imagine doing all of that in real time. Imagine doing that wrong in a UN session discussing climate change.

Google’s secretive 3D-mapping project now has a tablet

Google is keen to map the indoors. I see huge potential for indoor mapping. Imagine your interior designer mapping your house so that she can show you exactly how your renovated indoors will look? A robot that can go about your house tidying it? Are we heading towards becoming Wall-E lazy?

Apple announces iOS 8 at WWDC 2014

Easily one of Apple’s best WWDC considering all the news. A new programming language called Swift, Mac OS X Yosemite, Metal, HomeKit, CloudKit, and extensions in iOS 8. For those eager to learn Swift, Apple has provided an iBook for it already. A good news for all Netflix viewers, Safari on Mac OS X Yosemite now allows streaming using HTML5, no Silverlight required.