SYSTEM_ALERT_WINDOW on Android Wear

It’s a question I’ve seen a few times now on the Google+ Wear developer community and StackOverflow: How do developers acquire the SYSTEM_ALERT_WINDOW permission on Android Wear devices running Marshmallow?

tl;dr: You don’t, at least not for now. Keep your apps on targetSdkVersion 22.

But for those of you interested in the longer answer… This question affects my leading Wear app, so after seeing it come up a couple of times – and wanting to know the answer myself – I did some research. I posted my findings on StackOverflow, but decided that the topic could use a bit more explication, so I wrote this post.

First, a bit of background. Why should anyone, developer or otherwise, care?

Well, SYSTEM_ALERT_WINDOW is the internal name for an Android permission that lets one app draw to the screen superimposed over other apps. Android Police has a good explainer on the subject in the context of Android phones – but here, we’re concerned about smartwatches. And it turns out that this is really quite a handy thing to do on a watch, mostly because it allows apps to add functionality to the Android Wear homescreen, aka its watch face:

  • Wear Mini Launcher (and other third-party app launchers) let you swipe in from an edge of the screen to open their app list, or a quick-settings panel.
  • Similarly, Tappur uses a swipe-from-edge gesture to access actions, like turning smart light bulbs on and off.
  • My own Wearable Widgets uses a swipe in the middle of the watch face to move between widgets.

You may notice that all of these examples are about swipe gestures, and therein lies SYSTEM_ALERT_WINDOW’s power on the watch. By drawing an invisible window over the watch screen, we can detect touch gestures (like swipes), and initiate actions in response. It’s not an overstatement to say that this capability is central to the usefulness of many of these apps.

So, it’s useful. What’s the problem?device-2016-01-28-161527

Well, as mentioned earlier, SYSTEM_ALERT_WINDOW is what’s known as a permission, meaning that users have to specifically allow an app to do it. This is because it can be dangerous to let one app draw over others on your screen: the same capability that lets Wear Mini Launcher detect a swipe on your watch face can be turned to evil, letting a malware app detect the touch inputs you think you’re making on some other app: say, the password you’re entering in your banking app. Such an attack is known as tapjacking, and it’s led one Googler to describe SYSTEM_ALERT_WINDOW as “raised to be above DANGEROUS“.

In Android 6 (Marshmallow), Google overhauled the permission model so that users need to explicitly allow apps access to dangerous permissions – generally hailed as a long-overdue improvement. But what about permissions that are “above DANGEROUS”? Well, granting access to those takes an additional step; you need to go to a particular screen in the system Settings and flip a switch for each app that you want to allow.

Well, OK, that seems fair. Again, what’s the problem?

In a nutshell: Android Wear doesn’t have that system Settings screen.

device-2016-01-28-162633_framedWell, perhaps it has it; API level 22 on Wear did. But as of API 23, you can no longer get to it. The system doesn’t link to it (as it did on 22), and if you try to invoke it from a Wear app, you’ll get a crash. The Intent doesn’t resolve.

So what does that mean for those of us whose Wear apps rely on SYSTEM_ALERT_WINDOW? For the time being, I can only see one course of action: keep your apps on targetSdkVersion 22.

This works, because the new permission model is only enforced on apps that target Marshmallow, which is SDK level 23. This “solution” comes with a serious downside, however: your apps will be unable to use any of the cool new features that come with Marshmallow (or later versions). And some of these are seriously handy on Wear (like the ability to use -round resource qualifiers). But at this moment, I don’t see another way forward – though I’ll discuss longer-term options in a moment.

Before I do, however, I just want to note something for the developers reading this: keeping your Wear app on targetSdkVersion 22 means that the enclosing handheld app also needs to be on 22.

I haven’t noticed this restriction before, but I’ve tested it in this case, and it’s true. If your Wear app uses SYSTEM_ALERT_WINDOW and targets API level 22, but it’s packaged in a handheld app that targets 23, installation to the watch will fail. If you’re interested, the error message in logcat is:

W/WearablePkgInstaller: Wearable com.myapp has a permission “android.permission.SYSTEM_ALERT_WINDOW” that is not granted in the host application.

Which isn’t entirely truthful (this happens even if the permission is granted to your host app), but the result is the same: it doesn’t work. Don’t do it, kids.

So, moving on, what are our options for the future?

If we’re lucky, Google will add the Settings screen for users to enable SYSTEM_ALERT_WINDOW on the watch at some point, maybe when Marshmallow is released to the wider Wear ecosystem. You can (possibly) encourage them to do so by starring this issue in the external Android bug tracker. I have my doubts, though, as I get the feeling they are trying hard to discourage use of this permission, and may just cut it off completely on Wear. What if they do – what then?

The obvious (but wrong) answer is to keep our apps on API 22 indefinitely. At some point, however, this will become untenable. There will eventually be some new feature that your app can’t live without, or at the very least, someday you’ll want to move your minSdkVersion beyond 22.

The only other solution that occurs to me is to abandon swipe gestures for some other interaction.

One that I’ve experimented with in Wearable Widgets is tapping on the side of the watch (rather than its screen): by monitoring the device’s accelerometer, such a tap can usually be detected. My current code for doing so is somewhat primitive, and doesn’t work on some older, low-spec watches (with slow accelerometers), but it’s a start – and by the time it becomes necessary, hopefully there’ll be very few of these low-spec watches left in the wild. I intend to open-source my code for doing this soon.

If you have any other ideas, I’d be happy to hear them! Drop a comment below, or join the discussion on the Android Wear dev community.

UPDATE: (2 May 2016) My original testing of SYSTEM_ALERT_WINDOW on Android Wear was done using an API level 23 emulator, and the (recalled) second-edition LG Urbane LTE – the only publicly-released Android Wear hardware running Marshmallow at the time. Since then, the official Android 6.0.1 update has rolled out to all Wear devices, and I’ve confirmed that this issue still exists.

The larger issue also affects the other “super-permission”, WRITE_SETTINGS. It appears to be the same situation: the intent for the user to grant or deny the permission doesn’t resolve on Android Wear.

In addition, I’ve followed up a couple of related leads, which interested developers might find good to know about:

  • It’s emerged that apps installed to Android 6+ from Google Play are being granted SYSTEM_ALERT_WINDOW by default, simply by requesting the permission in their manifests. When I first read about this, I thought it might be a usable workaround for the problem that this post is all about… but no. I’ve tested it, and this grant does not extend to Wear. Apparently, even if the “host” handheld app is installed form Play, the Wear app isn’t considered to be – it still doesn’t get the permission
  • It is possible to grant SYSTEM_ALERT_WINDOW (and, I would expect, WRITE_SETTINGS) to a Wear app by installing with adb -g. I can’t see how this would be useful to end-user deployment, but might be handy for testing.

Bottom line is that we’re no further along in finding a resolution to this issue, so as far as I know, my workaround recommendations above still stand.

UPDATE: (19 July 2016) With the release of the second dev preview for Android Wear 2.0, this issue is now fixed. Firing a Settings.ACTION_MANAGE_OVERLAY_PERMISSION intent device-2016-07-17-213626 now opens a system window where the user can enable this permission. And as you can see, it applies to WRITE_SETTINGS too. Perfect!

Many thanks to the folks at Google who have shepherded the fix through.

Layout-based Watch Faces for Android Wear

Watch faces are basically live wallpapers for your smartwatch – this makes a certain sort of sense, and it is indeed how Google has implemented them on Android Wear. And like live wallpapers, they can be either Canvas or OpenGL-based. But while that works okay for most traditional live wallpapers, there are a lot of watch face designs for which neither of those models works overly well. Examples might include predominantly-digital faces, legacy faces (which were developed as Activities), faces that pack in a lot of data, and anything else with a fairly sophisticated layout.

What you’d like, in fact, is exactly that – to use a standard Android layout for your watch face. It turns out that you can, and in this post, I’m going to show you how. Which of course means that it’s long post, heavy with code. You have been warned.

We’re going to build our layout-based watch face on top of a CanvasWatchFaceService, and the key to it is that you can inflate an arbitrary layout into a View, and then call View.draw(canvas) to render a Canvas from it. The rest of the code is just window dressing, really, but some if it isn’t entirely obvious.

Before we get started, I just want to say that this guide is meant for programmers with some Android experience, and ideally a bit of experience on Android Wear. I’m not going to walk you through every last step of creating a project in the IDE, setting up and running an emulator, what ambient mode means, and so forth. There are lots of great tutorials online for those sorts of things, so I’m going to assume that you have those skills comfortably in hand.

With that said, it’s high time that we…

Get Started

Begin by creating a new Wear watch face project in Android Studio. The current version (1.2 as of this writing) includes watch face templates; we’ll use the Digital template for the example I’ll be explaining here.

[If you’re not using Android Studio yet, now’s a great time to start – and one of these days I’ll write up my hard-won guidelines for converting a mature Eclipse project over to AS. But today, we’ll be starting with a new project, so that’s not an issue.]

Here’s a walk-through of the project creation wizard. The details of these things tend to change from version to version of the IDE, so your screens may not look exactly like this, but hopefully they’ll be close enough.image10

Obviously, the app and package names are up to you.image06

Android Wear isn’t supported below API level 18, so it makes sense to choose that as the minSdkVersion on the phone/tablet side.

For this example, you don’t need an activity on the handheld (mobile) app, because it only serves as a container for installing the watch face. If the previous sentence doesn’t make sense to you, I suggest you go look at Packaging Wearable Apps for clarification.

On the Wear side, however, we do want an activity – or more precisely, a watch face:image04

And on the next screen, choose the Digital watch face template, and give it a name of LayoutFaceService:image00

When Android Studio has created the new project, go ahead and run it, just to make sure that you have a working starting point. It’s probably easiest to do so in an emulator, and when you do, it should look something like this..image03

Now that we have it working, let’s break it! This is an ordinary canvas-based watchface; it’s a reasonable foundation for what we’re going to build, but has some unnecessary bits. So, the next step is to clear those out. Delete the following from your LayoutFaceService source file:

  • The entire createTextPaint method
  • The contents of the onDraw method
  • The two Paint fields (mBackgroundPaint and mTextPaint) and all references to them in the code

And in the interest of brevity, I’m not going to walk you through cleaning up various other loose ends in the template (such as its use of the deprecated Time class). If it works at the end, we’ll call that good. Turning it into production-ready code is left as an exercise for the reader.

But before we move on, let’s create a few new fields that we’ll be needing soon. In the Engine class, insert the following:

private int specW, specH;
private View myLayout;
private TextView day, date, month, hour, minute, second;
private final Point displaySize = new Point(); 

Creating the layout

If all we wanted to render was a simple digital face (like the template), we wouldn’t need a layout – it’s easy enough to render directly. But the whole idea here is to render something more complicated, something that would be a pain to draw out directly onto the canvas.

On the other hand, I still want to keep it simple-ish for this example. image05Here’s a mockup of the watch face I had in mind when I started this post:

It’s just about complicated enough to make this technique worthwhile.

If you have any Android dev experience, you can probably create a layout like this in your sleep. I’m using a simple RelativeLayout, and you can find it in the source code for this example on GitHub. But really, the important thing to take away is that it’s in res/layout/watchface.xml, in my project’s wear module.image01

Initialization

Now we need to load that layout into our watch face service. The place to do so is in the Engine.onCreate method, and the code is as follows:

LayoutInflater inflater = 
    (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
myLayout = inflater.inflate(R.layout.watchface, null);

If you’ve ever worked with layouts in Android Java code, this will look familiar. It’s pretty standard stuff.

In order to use this layout, we’ll also need some data about the display, so put the following code in onCreate also:

Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
        .getDefaultDisplay();
display.getSize(displaySize);

specW = View.MeasureSpec.makeMeasureSpec(displaySize.x, 
    View.MeasureSpec.EXACTLY);
specH = View.MeasureSpec.makeMeasureSpec(displaySize.y,
    View.MeasureSpec.EXACTLY);

It looks complicated, but this is just boilerplate; you can copy this stuff verbatim into any layout-based watch face you build.

Finally, we’ll initialize the TextView fields to avoid having to find them every time we need to update the date and time. Again, this is quite basic stuff.

day = (TextView) myLayout.findViewById(R.id.day);
date = (TextView) myLayout.findViewById(R.id.date);
month = (TextView) myLayout.findViewById(R.id.month);
hour = (TextView) myLayout.findViewById(R.id.hour);
minute = (TextView) myLayout.findViewById(R.id.minute);
second = (TextView) myLayout.findViewById(R.id.second);

Making it work

In all Canvas-based watch faces, the onDraw method is where the pixels meet the code. Ours is no different, but one of the advantages to a layout-based face is that onDraw tends to be simpler than if we were drawing everything manually. In essence, we’ve front-loaded the drawing when we set up the layout, so we can offload the actual drawing code to the layout itself.

All of the following code goes in the onDraw handler. The first line is unchanged from the template that Android Studio generated for us:

mTime.setToNow();

That sets up the Time variable to the current instant. Next we apply it to all the on-screen date and time TextViews:

day.setText(String.format("%ta", mTime.toMillis(false)));
date.setText(String.format("%02d", mTime.monthDay));
month.setText(String.format("%ta", mTime.toMillis(false)));

hour.setText(String.format("%02d", mTime.hour));
minute.setText(String.format("%02d", mTime.minute));
if (!mAmbient) {
    second.setText(String.format("%02d", mTime.second));
}

These all follow a similar pattern, extracting a field from mTime and formatting it for display. It’s fiddly, but not actually hard.

So now, all our text fields have been updated, and we need to output it to the screen. But we’re not quite ready to draw it to the Canvas yet; first, we need to finalize the layout.

myLayout.measure(specW, specH);
myLayout.layout(0, 0, myLayout.getMeasuredWidth(), 
myLayout.getMeasuredHeight());

This is the crucial step to making a layout-based watch face work. If you’ve been doing ordinary Android development, it’s likely you haven’t seen code like this before; it usually happens behind the scenes in the View classes, and you don’t need to worry about it. But in our case, we’re using a “naked” layout – one not attached to a view hierarchy – so we need to do this ourselves. If we didn’t the layout simply wouldn’t render, and this technique wouldn’t work.

Whatever you do, don’t skip this step.

Now, we’re ready to output it to the Canvas:

canvas.drawColor(Color.BLACK);
myLayout.draw(canvas);

The first line clears the Canvas to black, and the second one draws the current contents of our layout.

And that’s it for interactions with the Canvas: usually the biggest part of onDraw, but reduced by the layout approach to a trivial line of code.

Making it work in the real world

At this point, we have a working watch face. It updates the display fields to the current time, and draws them to the screen.

Before moving on, I’d just like to note that much of the job has been done for us by the template itself. It contains quite a few pre-written methods for triggering updates every minute (when in ambient mode) or every second (when not), as well as more esoteric details like tracking time zone changes. Google has put all of this code into the template, and we don’t need to touch any of it.image02

Our watch face works! But, it’s not quite ready for release.

Ambient Mode

One key behavior of Android Wear watches is the two modes they operate in, ambient and normal. The latter is when the user’s actively engaging with their watch, and we’ve got that covered.

But a watch spends most of its time in ambient mode, and a watch face needs to behave differently when it does. The Engine class we’re working from includes a method, onAmbientModeChanged, which is where we’ll put the code to handle the changes.

First, the time-keeping infrastructure built into the template drops its refresh rate from once-per-second to once-per-minute, and as a result, we don’t want to be showing the “seconds” fields when in ambient mode. Because we’re using a standard Android layout, we hide and show the seconds the same way you would in any Activity:

if (inAmbientMode) {
    second.setVisibility(View.GONE);
    myLayout.findViewById(R.id.second_label)
            .setVisibility(View.GONE);
} else {
    second.setVisibility(View.VISIBLE);
    myLayout.findViewById(R.id.second_label)
            .setVisibility(View.VISIBLE);
}

The second change is a bit less obvious. The visual design I’m working from is based around chunky fonts, and I’ve used boldface type to achieve that effect. But that’s not a good idea when a watch is in ambient mode, for a couple of reasons.

  • AMOLED screens use less power when fewer pixels are lit, so boldface text probably uses twice as much power as normal text.
  • Some screen technologies have an issue with burn-in, and Android Wear addresses this by shifting pixels slightly on-screen. This works much better with thin lines, however, so watch face developers are encouraged to avoid thick strokes in ambient mode.

The upshot is, I only want to use boldface for my text fields when the watch is not in ambient mode. Here’s the code I use to do it:

Typeface font = Typeface.create("sans-serif-condensed",
        inAmbientMode ? Typeface.NORMAL : Typeface.BOLD);
ViewGroup group = (ViewGroup) myLayout;
for (int i = group.getChildCount() - 1; i >= 0; i--) {
    ((TextView) group.getChildAt(i)).setTypeface(font);
} 

You’ll notice that this is kind of a cheat, and only works because every view in my layout is a TextView. image07But it does work – here’s my watch face in ambient mode:

In most cases, more complicated layouts will require more complicated transitions between ambient and normal modes, but the details will need to be determined case-by-case.

Screen shape

In this example, I’m primarily designing for a square watch face. But in the real world, you’ll want to support round watches too – they make up the majority of Android Wear devices in the wild..

To avoid making this long post even longer, all I’m going to do to adjust this watchface for round screens is to shrink it a bit, so that the square text fits in the round hole. I’ll do that in two places; the first is the template’s existing onApplyWindowInsets method. Replace the code in that method (after the call to super.onApplyWindowInsets with the following:

if (insets.isRound()) {
    mXOffset = mYOffset = displaySize.x * 0.1f;
    displaySize.x -= 2 * mXOffset;
    displaySize.y -= 2 * mYOffset;

    specW = View.MeasureSpec.makeMeasureSpec(displaySize.x, 
         View.MeasureSpec.EXACTLY);
    specH = View.MeasureSpec.makeMeasureSpec(displaySize.y,
         View.MeasureSpec.EXACTLY);
} else {
    mXOffset = mYOffset = 0;
}

For round screens, we basically compute a margin – 10% of the square layout width – and apply it to the displaySize we got in onCreate. From there, we just recompute the MeasureSpec fields accordingly – this is determines the actual size of the layout.

We also need to use this margin when drawing, and that happens in onDraw, not surprisingly. All that’s needed is one new line of code, highlighted below, in the canvas-drawing section:

canvas.drawColor(Color.BLACK);
canvas.translate(mXOffset, mYOffset);
myLayout.draw(canvas);

As its name implies, translate just moves the Canvas before drawing to it, applying the offsets that we calculated above.image09

Here’s what it looks like on a round screen:

Nothing fancy, but it works. However, this is another case where the exact implementation details will depend upon your design. Besides just shrinking the design like this, other solutions might include moving elements of the layout around to fit, or using a different layout completely.

Our layout-based watch face is now complete, and just about ready to deploy. All that’s left are metadata elements like previews and icons, but these are no different than any other watch face, and don’t really merit coverage here.

Caveats

So far, I’ve mostly talked about the advantages of layout-based watch faces: primarily that you can use all of Android’s UI tools to build your watch face, rather than laboriously drawing it by hand (as it were).

But there are a few downsides as well.

Layout isn’t automatic

This is the biggest pitfall of this approach, for Android devs of all experience levels. Ordinarily, you create your layout, stick it in an activity, and it displays on screen. With a watch face, it’s not quite that simple.

I touched on this back in the onDraw method, but it’s important enough that it bears repeating. Remember these two lines of code?

myLayout.measure(specW, specH);
myLayout.layout(0, 0, myLayout.getMeasuredWidth(), 
         myLayout.getMeasuredHeight());

These two methods, measure and layout, need to be called after any change to your content that affects the size of any layout element. In my example, I ensure that it will happen by putting them in onDraw, right before drawing to the Canvas. But if you have more complicated logic – perhaps with different parts of your layout being updated in different places – you’ll need to take some care to ensure that measure and layout are always called when they need to be.

If you ever find that your layout is not rendering correctly, this is the first thing to look for.

Battery impact is not out of the question

I’ve developed (and deployed) several watch faces using layouts, and none of them seem to use an excessive amount of battery. However, battery life is always a concern on Android Wear, and it at least seems plausible that using a layout for your watch face will incur some extra overhead – and thus use more power.

As I said, I haven’t seen an issue with it yet, but given this is a new technique, I feel I should mention it.

And while we’re on the subject, you should take care to optimize your code for battery life generally. In the interests of brevity and clarity, I’ve taken a few shortcuts with this example (like updating the date fields every second) that you probably shouldn’t do in the real world. Google has some specific guidelines in this area; read them, follow them, and use your own good sense.

WatchViewStub doesn’t seem to work

A few paragraphs ago, I mentioned the possibility of using different layouts for round versus square screens. If you’ve done much Android Wear development, you’re probably familiar with a standard pattern for addressing this: WatchViewStub, which automagically selects the correct layout based on screen shape.

Unfortunately, WatchViewStub doesn’t work with the technique I’ve outlined here. It’s not open-source, so I can’t delve into the details of why not, but in my testing, inflating a WatchViewStub-based layout just doesn’t work. The magic smoke escapes somewhere, and you always end up with the same layout, regardless of screen shape.

Of course, you can still use different layouts for different shapes; this simply means that you need to select the proper one manually. And the good news is that onApplyWindowInsets does work, so you can easily get the screen shape there – and inflate the proper layout accordingly. Take a look at the Android docs (or the onApplyWindowInsets method in the template) for guidance on how to use this handy method.

Are layout-based watch faces for you?

It’s a fair question – they’re not right for every case.

If you’re creating a face that’s mostly built from graphical assets, it’s probably easier to render them directly to the screen, rather than shunting them through a layout first. Likewise, if your design involves moving many elements around the screen (such as analog clock hands, for example), using a layout may actually be more work.image08

On the other hand, if your watch face design is text-heavy, or has complicated interrelationships between the elements, or perhaps is based off an existing Android app – then using a layout probably is a good choice. And some operations that are a real PITA with direct rendering – like wrapping text – are just effortless with a layout.

It’s also worth noting that layouts aren’t an all-or-nothing proposition. It’s perfectly reasonable to draw some graphical elements directly to the Canvas, then draw your layout. I’ve used this hybrid technique in production watchfaces too.

In any case, I hope you’ve found this guide useful, and it helps you build better watch faces. The sample watch face I’ve built here can be found on both GitHub and Google Play.


Update 11 May 2015:

Fixed a bug with the onApplyWindowOffsets code for round watch face support.

What were they thinking?

urbane_lteWith its new Watch Urbane LTE, LG has gone with a WebOS derivative rather than use Android Wear, as all their other smartwatches do. What’s that about?

It seems clear that LG wanted a watch to compete with its arch-rival Samsung’s Gear S, which packs an LTE radio for voice and data access directly from your wrist. This is the headline feature of the Urbane LTE – it’s right there in the name. And since Android Wear doesn’t support cellular connectivity, LG needed an alternative. [The Gear S gets around this limitation by running Tizen, Samsung’s own mobile OS, on which it’s based most of its smartwatches to date.]

But even given that requirement, WebOS seems an odd choice. It’s hard to imagine that many developers will create new apps – or port existing ones – for an ecosystem that contains exactly one device. And without apps, any smart device faces an uphill battle for consumer acceptance.

Why didn’t LG just use Android? Not Android Wear, but a parallel fork of the AOSP. I can’t imagine that it would have been significantly harder than adapting WebOS, and it would have gone a long way toward addressing the app problem. There are currently thousands of developers building for Android Wear, which is 95% pure Android. We’ve already made our apps work on the smartwatch form factor; it would be a much shorter path for us to rewrite the Wear-dependent bits than to start over from scratch.

As an aside, though, there is one interesting aspect to the WebOS choice. As its name implies, WebOS is based on web technologies: “native” WebOS apps are actually little web pages, with functionality written in JavaScript. You know what other smartwatch OS uses the same architecture? Tizen. That’s right, the easiest watch apps to port to LG’s new shiny will be those written for its mortal enemy, Samsung’s Gear line.

All this is an issue because Google is maintaining much tighter control over Wear than it historically has over Android generally. This means that all platform-level innovation needs to come from Mountain View, rather than being driven by partners – and clearly, this is holding the ecosystem back. Major OEMS, like Samsung and LG, have already chosen to use other platforms rather than live within Android Wear’s limitations. There are two likely results of this situation.

First, Wear is unlikely to achieve the same kind of market-share dominance in wearables  that Android enjoys in mobile. Whenever a vendor does want to push the envelope, it’ll need to do as Samsung and LG have: use another OS. That fragments the wearable space.

And second, Wear is likely to be far less rich and diverse than Android has been on phones and tablets. We won’t get the experimentation, we won’t find any interesting new use cases that Google doesn’t see coming.

But in the wider world of wearables, innovation will happen. And when a bold new idea does resonate with consumers and take off – there’s a good chance it won’t be Android Wear doing it.

Testing In-App Billing on Android

I’ve spent the last few weeks extending Wearable Widgets with in-app purchasing (variously known as IAP, in-app billing, or IAB). Honestly, there’s no good reason why this should have taken weeks. I can immediately think of a couple of reasons why it has, though.

The first is security. Because IAB is a commercial transaction, and because some lowlife will always be looking to scam you out of two bucks, you need to apply various layers of security to it – and these add time and effort.

A paradoxical aspect of this is that you’re specifically advised not to use any code examples you might find online, especially those in the documentation, because the scum who crack these apps will have seen all those code structures before – and will probably have scripts ready to break it.

But on the other hand, it seems to me that there’s a real downside to asking myriad developers like me to roll their own IAB solutions. Most of us are developers with no significant security experience – meaning that our homebrewed solutions are probably riddled with security holes and rookie mistakes, making them much less secure than code that’s been developed by security professionals. It’s a bit of a programming paradox, really.

Further, Google also includes something they call an IabHelper, a pre-written wrapper around all of the IAB routines, and many of their examples are based around this helper class. But it seems to me that this flies in the face of the roll-yer-own security recommendation; I have to believe that any two-bit script-kiddie app-cracker knows right where IabHelper lives, exactly how to recognize it (even in obfuscated code), and how to exploit it. Seriously, why would Google even provide this?

But that’s not really what I want to write about here.

The topic I want to cover, the other area which has cost me a lot of time in my IAB implementation, is testing. Part of that is simply because it’s a complicated beast – OK, fair enough. But a big part of it is because the documentation for testing this stuff is about as clear as mud, if not actually misleading in some places. So I thought I’d share my experience in testing Google’s IAB as of mid-2014. Be warned, from here on in this entry’s going to be pitched at Android devs who are elbow-deep in IAB; I’m not going to explain every term along the way. If you’re not a developer, you may or may not find it worth your while to read on. But if you are in the target demographic, hopefully what follows will save you some anguish.

Google provides a couple of doc pages about IAB testing, and you should start by reading those. They lay the groundwork, and nothing they tell you is really wrong – it’s just not quite the whole story. But nonetheless, you should start there, and get some understanding of the lay of the land.

Now, I’m going to tell you the real story. Here’s the first gotcha: that first link above says that there are two ways to test your IAB app, test purchases and real purchases. And the page no sooner describes those two before it goes on to discuss a third way, static responses. So the main thing I’d like to do here is to lay out the three ways to test IAB, with the things you need to know about each. I’ll present them in the order you should do them; complete your testing with one before you move on to the next.

Stage 1: Static Responses

This is where you should start: write your first pass at your IAB implementation, unit-test all the routines involved, and send the transactions off to Google Play with an SKU of android.test.purchased. [You can also use .canceled, .refunded, and .item_unavailable to test unsuccessful responses. But let’s go for success right now.] These are the easiest tests, because you don’t need to upload or sign your APK in any way (other than the normal debug signature that the IDE automatically applies). These tests can also be run by any user – although apparently you’ll get incomplete information returned if you don’t test using your developer account, so I recommend sticking with a device registered to the same Google ID as your Play developer console. When you issue an IAB request with one of the static-response SKUs, the process will look something like this:

stage_1_1
  1. The purchase dialog Google Play shows will be completely dummied-up (see above).
  2. Assuming you’re using android.test.purchased, the transaction always succeeds.
  3. It returns to the calling activity’s onActivityResult() handler with a resultCode of -1, and a blank INAPP_DATA_SIGNATURE, but otherwise real values matching what you sent in the purchase request.

One important point I didn’t see anywhere in the docs: after you use a static-response SKU once, that SKU is now “owned” by the user that the device is registered to. And it will show as such in any subsequent IAB calls, which makes retesting a hassle. Apparently, it will reset by itself in a day or so, though I didn’t test this. I didn’t have the patience to wait a day between each debugging run of my app. But it turns out that you can also consume it in your app, with code like this:

IInAppBillingService.consumePurchase(
    3, context.getPackageName(), purchaseToken);

where purchaseToken is returned from the purchase request you sent. In my experience with static responses, the purchaseToken was a simple construct like “inapp:my.package.name:android.test.purchased”, but YMMV.

And that’s about it for static responses. When you’ve done all the testing you can with them, you’re ready to move on to…

Stage 2: Real SKU, test user

Update 10 May 2015:

Since I wrote this post 11 months ago, Google has completely overhauled their  “test purchase” procedure. At the least:

    • Apps can no longer be in draft on Google Play for non-static purchases; they must be published to some channel (alpha, beta, or production).
    • IAB transactions for apps published to alpha channels do not cost whitelisted users real money. This was how I expected things to work last year (see below); now, they actually do.
    • With the advent of Android Studio, it’s now possible to debug an app signed with a production cert. So handy!

I may do a new writeup on it at some point, but until I do, be aware that much of whatI did write in this section is no longer correct – and that Google’s docs are actually pretty reasonable now.

This is the case that Google calls test purchases, and it involves sending your real item SKUs but in a test mode. Everything is like a real purchase, except that no money actually changes hands. It sounds so simple in the documentation… but there are a couple of serious pitfalls.

First, you’ll need to get things set up on your Google Play developer console. You’ll also need to wait several hours after doing these changes for them to roll out through Google’s server network.

  • You’ll need to have created your IAB items (on the In-app Products tab of your app in the console)
  • The user ID(s) you’re going to test with must be whitelisted for IAB testing: Settings > Account Details > License Testing. Note that real SKUs can’t be purchased by the user who owns the Google Play account, even in test mode.
  • You need to upload a signed APK, with a higher versionCode (in the manifest) than anything you have published on Play.

When you actually do your testing, it will also need to be with a signed APK, and one that has the same versionCode as the uploaded-but-unpublished APK in the last step above.

The bad news here is that there’s no using the debugger at this stage: logcat is your friend. But the good news is that it doesn’t actually have to be the same APK as you’ve uploaded to the dev console; it just needs to have the same versionCode. This requirement – for an uploaded but unpublished version of your APK – is never really, explicitly covered in the docs. It’s mentioned obliquely, but nowhere near as directly as it needs to be.

If your test version hasn’t been uploaded to the APK tab on the console, the IAB call will fail. OTOH, if it is published at all – even in the alpha or beta channel – your test users will really get billed for the IAP.

This one took me days to work out: silly me, I thought the alpha channel was specifically for this kind of testing, and as long as I had the user’s Gmail address on the whitelist, the transaction wouldn’t post. Wrong. The whitelist is ignored for published APK versionCodes.

In fact, it’s even a bit worse than that – the whitelist is ignored for all versionCodes less than or equal to anything published. Let me illustrate that with a concrete example:

  1. The production versionCode of my app is 9 – that’s my baseline, before I started implementing IAB.
  2. After I had a first cut of IAB done – with versionCode 10 – I published it to my alpha-test group and had a whitelisted user try it. He got charged, because 10 wasn’t unpublished.
  3. Learning from these mistakes, I uploaded a later version (13) to Google Play, but did not publish it. I then emailed the APK to some whitelisted testers, who successfully used the IAB feature without being charged.
  4. Moving closer to release, I built versionCode 14 and published it to beta. I fully expect users of this version to be charged, and they are.
  5. However: the next day, one of my whitelisted test group from step 3 – still on versionCode 13 – tried out the IAB and got charged for it. My best explanation for this is that, even though 13 is still unpublished, the whitelist is ignored, because 14 is published.

The bottom line is, upload to Google Play but don’t publish anything at this stage. But once you have all those pieces in place, and have given them time to take effect, here’s how it’ll go:

stage_2_1
  1. The purchase dialog will be real, but with the extra “test order” line shown above.
  2. The purchase always succeeds, AFAICT
  3. The return values are all real (token, payload, etc)
  4. The transaction appears in your Google Wallet Merchant console with a “Test:” prefix

As with static responses, this SKU is now owned by the user, causing a similar headache for any retesting you need to do. You’ll need to cancel it from your Merchant console (or alternatively, it will apparently expire in a week or two).

Be aware that, in addition to cancelling the purchase, you’ll also need to clear the cache for Google Play Store and Play Services on the device – and probably reboot, and maybe wait an hour or so – before the SKU will stop showing up as owned.

Stage 3: Real Purchases

This isn’t really “testing” IAB any more; everything is live, from the APK down to the financial transaction. But Google includes it on their testing page, so I’ll discuss it briefly here as well. Basically, this is the scenario your user will be in whenever the versionCode of the APK they’re running matches one that’s published on Google Play, in any channel (Alpha, Beta, or Production). It’s also the way the purchase will play out if the user isn’t on your IAB, or the whitelist entry hasn’t percolated through the Googlenet.

As confirmation of any of these situations, any time Google Play shows a purchase dialog without the “test order” verbage (compare the above and below screenshots), the user will get charged for the purchase.

 stage_3_1

I’m not going to go through the whole process here, because it’s a live transaction, and everything happens accordingly. If you do need to retest in this case, its like Stage 2: clear the cache for Google Play Store and Services, reboot the phone, wait an hour.

Time Is Money

One recurring theme you might notice from the above is all of the waiting that goes on. From hours to weeks, there are many, many aspects of IAB testing which require you to just twiddle your thumbs (or go read Engadget) for extended periods. Which is why testing this has taken so damn long. As of today, I’m really not sure that IAB was worth doing, especially for (what should have been) a dead-simple implementation like mine: I have exactly one product, essentially an unlock key to turn the “trial” version of my app into the “full” version. There are several other tried-and-true ways to implement this sort of freemium model, and I’m not at all convinced that IAB was the right one to choose. Sure, it may be slightly lower-friction for the users – but enough so to justify weeks of extra developer time? Will it actually gain that many more conversions? It’s impossible to know.

What if the war is over?

According to analyst figures, Android is hovering at around 80% of mobile device shipments worldwide. iOS accounts for most of the remainder, with about 15%, leaving roughly 5% for everyone else to fight over. And fighting they still are, but at some point everyone else has to ask themselves: is there any justification for continuing? It’s looking more and more like future technologists will recall this decade and say of some particular date, “That’s the point when the mobile platform war was won by Android.” Like how we can now look back on, say, World War II, and make pronouncements like, “The Allies had essentially won by the time of the Normandy invasion in June 1944. Germany just hadn’t admitted it yet.”

My hypothesis is that, right now, early 2014, that point in mobile has already passed. We’re just too close to the battle lines to see it.

This hypothesis may be wrong, of course, but here are a couple of indications that it may be right: The most successful tablet platform after Google and Apple is Amazon – and it’s Android. The most interesting, talked about, up-and-coming mobile platform of today (in my opinion) is Cyanogen Inc. – and it’s Android. In other words, the platforms lining up after Android and iOS… are Android. That’s not proof, sure, but for the rest of this article I’m going to explore some of the consequences that I would foresee if the hypothesis were true.

If my hypothesis is true, it means that we’re moving into a place – or rather, realizing that we’re already in a place – where it can be safely assumed that any mainstream mobile device (not made by Apple) is running Android. Because, what else would it be running? Think of the PC market any time after about 1992: there was never any question that a given Dell, Gateway, or Compaq would be running Windows. Sure, Apple was doing their own thing, and there were any number of niche platforms, but for the real mainstream, there was only one. Forget competing ecosystems, that’s a fantasy.

I’m certainly not the first to draw a parallel between the mobile platforms of today and the PC platforms of decades past, with Google filling the role held by Microsoft in those days, and Apple as, well, Apple. But that’s the dynamic that always gets the attention, Apple-as-underdog vs. The Establishment. Apple’s got experience in that role, and is apparently happy with it, that’s what they do. We don’t need to worry about Apple. But what about everyone else?

Everyone else, in this case, falls generally into two camps. First, there will always be niche platforms nibbling at the periphery, filling the needs of some particular corner of the market. Like how the various Linux distros do on PCs – and sure enough, open-source analogs to those are appearing in mobile, like Ubuntu and Firefox. It’s always possible that one of these may break out into the mainstream, but for now, I think we can safely call them niche platforms. And by definition, they don’t affect the big picture very much.

The second camp contains the dinosaurs, the platforms which were big players in the pre-iOS world and which didn’t evolve quickly enough to stay competitive. Nokia has already fallen, sold itself out, admitting that it had lost the battle. The others that are still holding on are BlackBerry and Microsoft, of course: both have lost the large market share they once had, both have reinvented their platforms as “modern” alternatives – and both have completely failed to get any significant traction. And both are still in denial about having lost the war, both still think that they can claw their way back to relevancy.

If my hypothesis is correct, it’s already too late for either of these contenders, and has been for some time.

The writing has been on the wall for BlackBerry for years now, and although they’re hemorrhaging money and employees (not to mention CEOs), they still talk like they can win this thing. Does anyone outside – or even inside – of Waterloo actually believe that any more? BlackBerry 10 is a fine OS, and its current hardware is more-or-less competitive, but that’s like saying “Germany could still win in 1945 because they built good tanks.” I’m not going out on a limb at all when I say that BlackBerry, as it was, is finished.

What can this particular dinosaur do to avoid complete extinction? The first step is to admit defeat, and ring down the curtain on the BlackBerry OS. Then bring the quality BlackBerry hardware over to Android – it’s the only option now, remember – and work to leverage whatever other strengths BlackBerry Ltd. has left. I’ll bet it still has good supply chain connections, and some lingering relationships with carriers. If necessary, it could build an emulation layer to run legacy BlackBerry apps on Android (they’ve already done the reverse), as a bridge for existing customers.

Beyond that, a good strategy would be to play the security card that BlackBerry was once renowned for. Deserved or not, Android has a reputation for being wild and insecure – BlackBerry could conceivably build a locked-down ‘droid that would offer execs (and others) a secured experience. Better yet, BlackBerry could buy that expertise: Geeksphone has recently announced its Blackphone project along those same lines. Even the name’s a good fit. I’ll bet BlackBerry has enough left in the bank to aqui-hire Geeksphone, kick-starting Waterloo’s Android conversion and even picking up some long-lost geek cred in the process.

The other dinosaur in the room is Microsoft… and it’s complicated. They’re too diversified to get dragged under by the failure of their mobile platform; they can keep it on life support indefinitely if they so choose. But on life support it most certainly is: in the last quarter of 2013, sales of Nokia’s Windows Phone handsets fell by 7% – in the quarter that includes the Christmas buying season. And Nokia is almost the only one making these phones any more; if it wasn’t for Microsoft buying Nokia’s hardware division, there’d hardly be any Windows Phones at all these days. You can be certain that this battle is lost if the oft-rumored Nokia Normandy, running Android, comes to pass – a sure sign that Microsoft has admitted their defeat. [Did you say Normandy? What was that about WWII earlier?]

But that’s not happened yet. Right now Microsoft is also in the search for a new CEO, and the direction that the new leader takes will tell the tale here. S/he might double down on Windows Phone, and make a last shot at relevancy with a big shakeup. Buying T-Mobile might just do it (think about that one for a minute).

But probably not. My thesis is that the war’s already over, right – and if so, what’s Microsoft’s place in the post-war world?

One option is to just get out of the phone business entirely. Sell off the remains of Nokia (who’d probably go on to build some great Android phones) and fall back to the businesses that Microsoft knows, and more-or-less dominates. Take their ball and go home.

If we’re truly moving into a post-PC era, though, Microsoft won’t want to sit that game out. So again, the first step is admitting defeat, and learning to live under the rule of the victors. Let the Nokia division build those great Android phones, and build its own place in the Android market – a place that’d probably be significantly larger than its Windows Phone market is now.

As for Microsoft proper, well… At its core, Microsoft is a software company, and you know something? Its software could actually run on someone else’s operating system. Here’s a plan: Release Office (and other apps) for Android. Retool its server products to work well with Android clients. Build Android integration into XBox. Better yet, do all of these things for iOS and MacOS too: Apple and Google could be its partners instead of its competition. There’s no intrinsic reason that Microsoft has to control the whole software stack, it’s just done that for so long that everyone (especially in Redmond) expects that’s their rightful place in the world.

Which brings us to the big opportunity here: if the war is over, the combatants can stop fighting and start cooperating. Can build great things together. Think of it as a mobile platform peace dividend.

Afterword: Through most of this article, I’ve been drawing parallels between the mobile market of today and the PCs of yesterday. But it hasn’t escaped my notice that the PC market is still around (if shrinking), and may be undergoing a real shakeup for the first time since Microsoft established their dominance. To wit: I wrote this piece on Chromebook… indicating that it’s never too late to introduce a new platform contender.

What’s Next for Wearable Tech

The news out of CES last week highlights the growing profusion of wearable devices: smartwatches are the most widespread, but there are also smart glasses, earphones, bracelets, rings, and more. It’s a real “wild west” time for these devices; nobody really knows where they’re going, or what the best use cases are.

For developers, the situation is especially acute, because essentially every device is siloed right now. If you have a good idea for a wearable app, you need to write it for Pebble, rewrite it for sony_sw2Sony’s Smartwatch, again for Google Glass, and so on. It’s true whether you’re building primarily for the device itself, or even just trying to interface your existing phone app with a wearable display: each device has its own little ecosystem. Some of the more shortsighted manufacturers haven’t even opened up their devices to outside devs (I’m looking at you, Samsung).

It’s not dissimilar to the state of the phone business circa 2005. Most phones of the time supported apps, and there were even a few smartphones around, but they were all siloed in this same kind of way. If you wanted to deploy a phone app to a good-sized audience, you had to write it for Blackberry, then rewrite it for Windows Mobile, JavaME… and don’t get me started on Symbian, where Nokia couldn’t even maintain compatibility within their own product line. It was a mess.

Even within each of these platforms, there wasn’t a good, low-friction way for devs to sell and deploy their wares. The different silos may or may not have had “stores”, but those that did were all pretty small, and app discoverability and monetization was a real challenge. And again, this is mirrored in the current state of wearables: Sony’s Smartwatch pairs with Android phones, so they’ve piggybacked off the Google Play store. Pebble, on the other hand, is just opening a store of its own. Glass has a small selection of officially-sanctioned apps curated by Google, but it’s far from easy to break into, and (as of right now) you can’t charge for your apps there. And so on… it’s a mess.

The first thing that changed in the phone business, of course, was the introduction of the iPhone. Apple was the first vendor to really nail it, not just the device, but the app store too. And for the devs, it was a veritable gold rush. This is one route that the wearables market could take: one vendor could “get it right”, have a device that’s a breakaway success, and build a big enough silo to be self-sustaining. You get a definite sense that most of the current players are hoping for this kind of outcome, but don’t know how to get there.

But there’s another route, one that we’ve seen before in the phone business. I’m talking about Android, of course: although it was released over a year after the iPhone, and took another couple of years to really pick up speed, it’s become the dominant ecosystem in mobile. And it has done it in large part by not being siloed to a single vendor; its openness has unified the vast majority of phones and tablets onto a single platform. The wearables market doesn’t just need something like Android to unify it, it needs Android. And essentially all the pieces are already in place, if you know where to look…

To start with, Google already has one of the leading wearables – Glass – and it’s running full Android on board. Google has also bought one of the pioneers of the modern smartwatch era – WIMM Labs – and rumors about a forthcoming “Google Watch” have been rampant for a couple of years now. It’s not really a secret that Google is moving into wearables, but I’ve seen little analysis about the bigger picture of that move – not just about Google selling a smartwatch or Glass to the general public, but about the broader ecosystem play. So here’s how I see that playing out.

Sometime this year – most likely at I/O – Google will take the wraps off Android 5.0, let’s call it Licorice. Licorice will give Android full, native, first-party support for wearables, in much the same way that Honeycomb brought first-party tablet support to what had previously just been a phone OS. [Sure, there were Android tablets pre-Honeycomb, but they were half-baked, not-overly-successful forays into unsupported territory. Remember the original 7″ Galaxy Tab, back in 2010? That’s what the Galaxy Gear is now.]

The wearable support features introduced in Licorice will include:

  • A new screen size bucket, for screens less than 2″ in size, which will extend the lower end of the existing small-normal-large-xlarge Android screen size range. Alternatively, Google could extend their newer minimum-width-and-height resource selectors with maximum-width-and-height descriptors, but that seems clumsier to me.
  • A set of new UI conventions and development paradigms appropriate for small, glanceable displays without keyboards. Want to see a preview? Look at the Glass Development Kit (GDK) introduced last month. It’s all there, and most of it would work equally well on most any wearable, not just on Glass.
  • New low-power optimizations, quite likely in cooperation with one of the major ARM vendors. This actually already started with support for Bluetooth LE in Android 4.3.
  • Wearable-specific extensions to the Play app store, analogous to the improved tablet support that Google brought to Play in 2013.

The point here, of course, is to extend the Android phone-and-tablet ecosystem to include wearables as first-class citizens. And this will enable a whole new generation of Android wearable hardware. Glass is the forerunner, but for everyone speculating about of “the public release of Glass” or “the Google watch”… you’re thinking too small. We need to be thinking about a whole range of such devices, from a variety of manufacturers. Some will bear the Google name, but certainly not all.

I expect that the first of these devices (after the current Glass XE) will arrive at the same time as Licorice, probably also with announcements at I/O. We’re getting further into speculative territory here, but I think we can make a good conjecture from Google’s past hardware ventures – essentially, projecting Google’s known hardware tendencies into the this next realm. There are some definite patterns to the hardware Google has used to introduce other new ventures – phones, tablets, and laptops (Chromebooks) – that will likely also apply to wearables.

  1. The pioneer hardware, designed and built with Google’s close supervision, defines Google’s role in a new space. This is essentially a public hardware beta, whether or not it’s openly acknowledged as such, and is usually a bit clunky and prone to widespread criticism as “an unfinished device, for early adopters only”. Well duh.
    • Phone: G1
    • Tablet: Moto XOOM
    • Chromebook: Cr-48
    • Wearable: Glass XE
  2. The second wave is populated by other OEMs, producing variations on the original hardware, more or less independent but still running Google’s OS. This crop of devices begins the move into the mainstream, and historically, has included the early Android and ChromeOS devices from all the major manufacturers.
  3. After a year or two of this, Google weighs in again, producing a device that “sets the standard” for where the big G thinks the ecosystem should be going. The Nexus One, Nexus 7, and Chromebook Pixel all fit this description.
  4. Everyone iterates, of course, Google and OEM alike.

The first thing to notice in the Android wearable space is that we’re still in phase 1 of this progression. Glass is the only official-Android wearable device we have so far, and the Explorer program is nothing if not a public beta – we’ve got a long way to go. I think Google’s Web DNA includes a strong “release early” tendency, and that applies to hardware too.

And if other Android wearables follow the same sort of pattern, then they’re even further behind. The first “Google watch” we see is likely to be a hardware beta comparable to the G1 or Cr-48: clunky, unpolished, with just a glimmer of its true potential. As I said, it’ll probably be announced at I/O 2014 – hell, it’ll probably be given away to attendees – but don’t expect it to be an instant game-changer. Google plays a longer game than that, and wants you to be a part of the process.

Ready for another gold rush?

AFTERWORD: What about Apple?

Apple doesn’t play the same game as Google, of course, but the introduction of the iWatch is at least as eagerly anticipated. The thing is, Apple thinks different: it develops its hardware in strict secrecy, and doesn’t release until it’s been polished within an inch of its life. Compare the G1 to the original iPhone, the XOOM to the iPad, the CR-48 to the Macbook Air. A Nexus or Pixel can go toe-to-toe with its Cupertino counterpart, but that’s only after a couple of years of public hardware iteration.

The point being, Apple won’t release an iWatch until it’s good and ready. Could be anytime, could be years down the road.

The other thing about Apple is that it doesn’t tend to join a rough-and-tumble, free-for-all market like wearables are in right now. It tends to let other companies make the early mistakes, learn from them, then release a product that overcomes the widespread issues affecting the first generation. If this pattern holds, Apple might not release its iWatch for some time yet. You can bet they’re watching the space closely, though, and refining their own plans all along.

The API of Unintended Consequences

A change to the Android API that arrived in the KitKat is that alarms don’t necessarily arrive when your code requested them to. From the docs:

Note: Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; seesetWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.
Now, many apps won’t be especially bothered, but I have two apps with clock widgets that rely on the alarms being delivered exactly on time; otherwise, the clock widget is wrong until the alarm does get delivered. Which means that my widgets now need to use setExact() rather than just set().

Which is fine as far as it goes – but what’s not mentioned above is that there is no setExactRepeating(). Here’s another extract from that same page:

Note: as of API 19, all repeating alarms are inexact. If your application needs precise delivery times then it must use one-time exact alarms, rescheduling each time as described above. Legacy applications whosetargetSdkVersion is earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact.”

This means that my widgets must now reschedule their alarms every minute. Which means my widgets are now running more code every minute than they were before. Which means that my widgets are now using more battery than they were before – exactly the opposite of Google’s stated intent with this change.

Footnote #1: I should also mention that, in my testing, these inexact alarms occur on KitKat devices even if targetSdkVersion is set to 18. That’s not what the docs (quoted above) say, but that’s how it works on my Nexus 7.

Footnote #2: No, I can’t use AnalogClock and Chronometer to avoid these alarms (and use less battery). They’re too limited for my widgets. My analog clocks are 24-hour (the AnalogClock class is 12-hour only), and the Chronometer class really isn’t suitable for digital clocks.