The Case of the Missing Bluetooth Address

Android 6.0 (Marshmallow) was released about 4 months ago and it brought some welcome changes - such as runtime permissions - as well as some not-so-welcome changes - such as the unceremonial removal of the BluetoothAdapter.getAddress() api.

The History

Prior to Android M, getting the Bluetooth address was a relatively simple affair:

BluetoothAdapter adapter = getAdapter();

if (adapter != null) {
    String address = adapter.getAddress();
} else {
    // Device does not support Bluetooth
}

Unfortunately, advertisers have been abusing this api to uniquely identify users in a way that would allow them to continue to track a user even if they uninstalled and reinstalled an app. Since the Bluetooth address comes from the device hardware, they are even able to stay on the trail of a user that factory reset their phone! Which is why BluetoothAdapter.getAddress() and WifiInfo.getMacAddress(), the two apis that gave developers access to hardware identifiers, were removed in Marshmallow.

The Problem

The abuse cases were bad and I’m glad Google is putting a stop to it, but there were legitimate uses for these apis and abruptly removing them has caused problems. Instead of throwing an exception and making the application crash, Google chose to return a dummy address - 02:00:00:00:00:00 - which is fine, if you are using the address for tracking purposes.

Apps that are using it for tracking will suddenly see a ton of activity from a “single” user, realize what has happened and filter out that data. Eventually they can update their app to track users a different way.

Legitimate uses of the Bluetooth address rely on BluetoothAdapter.getAddress() returning the actual, working Bluetooth address for the device. For instance, SocketScan uses the Bluetooth address during the scanner pairing process.

The way it works is that we display a barcode on the screen that contains the Bluetooth address of your Android device, when the scanner reads that barcode, it reconfigures itself and starts trying to connect to the Bluetooth device at the address it just received. It is a simple trick to make the scanner initiate the connection even though there is no screen on the scanner for you to select the device to which it should connect.

The problem with Google’s quick fix is that legitimate use cases of the Bluetooth address don’t just fail, they fail silently.

The Fix

Unfortunately, it appears the only way to get the Bluetooth address of a marshmallow device is to ask the user. However, there are a few things that can smooth the transition.

Cache the Bluetooth address while you still can. Marshmallow just crossed the 1% threshold of active Android devices, so there is a good chance your app still has access to the Bluetooth address. If you cache the Bluetooth address now, you can still access it after the device is upgrades to marshmallow. We are using a wrapper class that tries to retrieve the Bluetooth address from shared preferences before trying the adapter and if the adapter returns a valid address (i.e. not 02:00:00:00:00:00) it saves the address to shared preferences before returning it to the caller.

Make it easy for the user. Bluetooth addresses are not user-friendly. They are difficult to type and even more difficult to remember. Even though the user has to provide your app with their device’s Bluetooth address, it doesn’t mean they have to type it. We link the user directly to Settings > About Phone > Status where they can long-press the Bluetooth address† to copy it to their clipboard and paste it into the EditText when they return to our activity.

† The Bluetooth address is only available, if Bluetooth is ON.

Show me the code

Our BluetoothUtils is available as a gist.

A Little Git Trick

Ever find yourself wanting to check the status of every git repo inside a particular folder? Of course you do.

# OSX or FreeBSD
find . -type d -name .git |
  xargs -n1 dirname |
  xargs -I {} git -C {} status
# GNU variants
find . -type d -name .git -printf '%h\n' |
  xargs -I {} git -C {} status

status can obviously be replaced with the git command of your choice. For instance, log -1 --pretty=oneline

Requires git 1.8.5

Angular Boilerplate Plus

Most of the Angular tutorials online use trivial examples to illustrate a very specific point and don’t provide any guidance on how to structure an Angular project. Unless your app is limited to one controller and one directive, it’s something you’ll need to think about. Fortunately, Josh D. Miller published Angular Boilerplate so we don’t need to think too hard.

What Angular Boilerplate, or ngbp, does is it tries to set forth some best practices for structuring a non-trivial angular apps. (Ironically, ngbp is not a sample app, so it provides a trivial app to demonstrate how to structure a non-trivial app.)

Building (grunt)

Out of the box ngbp gives you some handy grunt tasks.

grunt build - Builds/compiles less and coffeescript into css and javascript, respectively. Converts your templates into js to reduce the number of queries the application makes to the server. It copies everything into the build folder and runs your tests.

grunt watch - Builds then rebuilds if it detects any changes to the source files (i.e. files in src/ and vendor/). The caveat here is that it will only detect changes to existing files. If you add a new module or template, you’ll need to stop and restart grunt watch.

grunt compile - Concatenates and minifies all the css and javascript files in your build folder into one neatly packaged css and javascript file in your bin folder.

Routing (ui-router)

ui-router let’s you organize your app and urls around states, instead of trying to organize your app around urls. States can be nested and child states inherit their parent’s scope. Functionally this means that you can define a parent state which lists all of the objects in a collection and clicking an object transitions the user to a child state (for example, a detail view of the selected object). The child state has access to the collection defined in the parent state, so you don’t need to re-fetch the object from the server to display it. Changes made to the object in the child state are reflected in the parent state when the user transitions back to it.

Compiling (ngmin)

When I started with ngbp, I was surprised that the sample modules weren’t annotated. Everybody knows you need annotations if you want to minify your angular app! Right? In my naiveté I started adding all the missing annotations.

// Breaks when minified
angular.module('myMod', [])
.controller('MyController', function($scope) {
    $scope.key = value;
});

// Works when minified
angular.module('myMod', [])
.controller('MyController', [ '$scope',
    function MyController (    $scope) {
    $scope.key = value;
});

// I even line up the names to make sure I've got them all

It turns out ngbp wasn’t raised by no fool and you don’t need to write annotations, because the annotations are added automatically by ngmin during the compile step. Very clever! Except ngmin doesn’t annotate all dependency injections, most notably, those injected into resolves in a ui-router state.

angular.module('myApp.area', [ 'ui-router'] )

// Annotated by ngmin
.config(function($stateProvider) {
    $stateProvider
    .state('myState', {
        url: '/items/:id',
        controller: 'StateCtrl',
        resolve: {
            // NOT annotated by ngmin
            item: function($http, $stateParams) {
                return $http.get('/api/' + $stateParams['id'])
                .then(
                    function success(response) {
                        return response;
                    }
                )
            }
        }
    })
});

Fortunately, there is a simple fix: ng-annotate. ng-annotate has nominated itself to be the successor to ngmin and has the backing of the author of ngmin himself, so it’s probably just a matter of time before ngbp ships with ng-annotate, but until it does, you’ll need to switch manually.

`# Remove ngmin and it's dependencies`
npm uninstall grunt-ngmin --save-dev
`# Install grunt-ng-annotate and it's dependencies`
npm install grunt-ng-annotate --save-dev
`# Update the grunt tasks to use grunt-ng-annotate`
sed -i "" -e 's/grunt-ngmin/grunt-ng-annotate/g' Gruntfile.js
sed -i "" -e 's/ngmin/ngAnnotate/g' Gruntfile.js
`# Not strictly necessary, just updates a comment`
sed -i "" -e 's/ng-min/ngAnnotate/g' Gruntfile.js

Settings (environment variables)

It is not uncommon to use different variables in different environments. For example, a local api endpoint for development and the real api endpoint for production. While ngbp doesn’t support it out of the box, it’s easy enough to add yourself.

  1. Create your environment specific configuration files
// file: src/conf/dev.js
angular.module('myApp.settings', [])
.constant('config', {
    'html5Mode': false
});
// file: src/conf/prod.js
angular.module('myApp.config', [])
.constant('settings', {
    'html5Mode': true
});
  1. Use the values in your app
// file: src/app.js
angular.module('myApp', [
    'myApp.config'
])
.config(function($locationProvider, settings) {
    $locationProvider.html5Mode(settings.html5Mode);
})

...

;
  1. Set a default environment
// file: build.config.js
module.exports = {
    ...
    env: 'dev',
    ...
  1. Update your build task
// file: Gruntfile.js

...

// Rename the build task to ngbpbuild
grunt.registerTask( 'ngbpbuild', [
    'clean', 'html2js', 'jshint', 'coffeelint', 'coffee', 'less:build', 'concat:build_css', 'copy:build_app_assets', 'copy:build_vendor_assets', 'copy:build_appjs', 'copy:build_vendorjs', 'index:build', 'karmaconfig', 'karma:continuous'
]);

// Create a new env aware build task
// Sets the env and calls the old (renamed) build task
grunt.registerTask( 'build', function(env) {
    env = env || grunt.config.get("env") || "dev";
    grunt.config.set("env", env);
    grunt.task.run("ngbpbuild");
});

...

Conclusion

I’d recommend ngbp as a starting point for any angular app, even trivial ones, because weren’t all non-trivial apps trivial at some point?

The magic of ngbp is in the Gruntfile. By automatically discovering and adding project files to index.html during grunt build, ngbp makes it easy to create and maintain a sensible project structure, even if it’s not the structure they prescribe. And ng-annotate and grunt watch, a grunt task that detect changes, rebuilds the project and reloads your browser, eliminate a lot of the repetition of angular development. Good project structure and automating repetitive tasks are a recipe for greater productivity and ultimately that’s the goal-to be productive.