Inbox Ten

Because 10 > 0

I’ve heard people recommend Inbox Zero as a way of managing your email – that you try to get and/or keep zero messages in your inbox.  I’ve tried it, but I’ve always struggled, because there always seem to be a couple of emails that I’m not ready to file or delete just yet.

So I’ve come up with a solution that has been working pretty well for me for over two years – once a week, I get my email inbox down to ten or less.

Is this a joke?

No, it’s not a joke – it’s a reimagining of the goal.  The point of Inbox Zero isn’t actually to get to zero emails, it’s to keep your inbox manageable.  Ten is just as manageable as zero, and so much easier to achieve.

What Stays?

For me, if I have a shipping notice for something that hasn’t arrived yet, I may keep that around as a reminder to look for a package.  If I have an email I need to respond to, that might stick around.  If I have an email with information that I’m going to need in a couple of days, I’ll leave that in the inbox until then.

Why does this help?

This helps because it’s more doable.  If I have 50 emails, there are probably 35 that are easy to process or file, 10 that take a few minutes but are doable, and 5 that are going to take some serious work, or are blocked.  If I can get through the 35 easy emails and 10 mediums, then I’m done.  I’ve winnowed my inbox back down to a manageable amount without having to get rid of emails that I actually want to keep around.

If it’s easy, you’re more likely to stick with it.

Give it a try

If you’ve tried Inbox Zero but never been able to make it stick, try getting to Inbox Ten this week. 

Reverse Engineering on the Device – Slides from Droidcon UK 2019

I have the opportunity to present an expanded version of my talk about reverse engineering Android apps on the device, at this year’s Droidcon UK.

The video is available on the SkillsMatter website.

Links:

You can also see the slides & video from a shorter version of this talk at Droidcon NYC 2019.

Reverse Engineering on the Device – Slides from Droidcon NYC 2019

At this year’s Droidcon NYC, I had the chance to talk about some oddball techniques for reverse engineering Android apps from another app on a device.

Thanks to everyone who came out, and the slides are available now.

Where is your team’s center of gravity?

As more workers push for remote work, we’ve begun trying to find a common vocabulary to classify how well an organization supports its remote workers.  There’s the idea of “remote-friendly” vs. “remote-first” — how do your teams tools and processes enable your remote teammates to succeed?  The problem is that these terms are very subjective – it’s hard to assess yourself well with this standard.

I have worked on teams with various remote and distributed configurations, and there’s one factor that seems to determine whether remote employees can succeed — I’m calling it the “center of gravity.”  Where is your team’s center of gravity?  Let me show you a few examples.

The nominally “remote-friendly” team

If the majority of the team is in the office, the center of gravity is in the office.

You’ve decided to be remote-friendly, which means letting a few of your good people move back to their hometown, or closer to family, or just stop coming into the office.  But with the majority of the team still in the office, your center of gravity is in the office.  This sets you up for failure.  The majority of communication is going to happen in the office over a cubicle wall, or over coffee.  When you have a meeting, everyone in the office huddles in a conference room around one shared microphone while the remote employees struggle to hear, and can’t keep up.  This type of “remote-friendly” is anything but.

The majority-remote team

Another straightforward example: with most of the team working remotely, your center of gravity is now in remote-land.  Your default modes of communication move to remote-friendly tools. Instead of conversations over the cubicle, they’re happening in Slack.  Instead of the conference-room-plus-crummy-audio situation above, everyone calls in from their own desk, setting a level playing field. 

They may have lost the center of gravity, but people in the office aren’t put into a weaker position, relative to remote workers.  They have the same access to the same tools as the remote workers.  Everyone can contribute, everyone can be heard.

This works well, but a lot of companies think they can’t get any work done with most of their workers out of the office. There is, however, another configuration I’ve seen, and the results may surprise you.

The distributed team

This is a distributed team.  It has the same amount of remote workers as the first example (the in-office center of gravity) except now the in-office contingent is split up among several different offices.  Our visual metaphor breaks down a bit here, because the new “center” just depends on how we arrange the faces. So instead of taking the top-down view of the team, let’s draw that same team from any one worker’s point of view:

Even though the majority of people are in an office, from the perspective of any individual teammate, the majority of the team is somewhere else.  This forces the team to adopt remote-first tools and processes, even with a mostly in-office team.

This theory is based on my own observations, which are admittedly a small sample size.  Have you seen something different? Tell me about it.

Thanks to my colleague Mike Rooney for suggesting an improvement to this post.

Save time and reduce risk with Gradle’s includeGroup

With the recently released version 5.1, Gradle has added a great, subtle new feature that lets you specify which dependencies should be pulled from which repositories.  To explain what this is, let’s start with the default behavior. In your Gradle file, you probably have multiple repositories defined, like this:

When Gradle needs to find a dependency, it will search each of those repositories, in the order they are declared.  So when it goes to download, for example, [com.android.support.constraint:constraint-layout:1.1.3], it will first check the Google repo, which has that artifact, so it’s done.  But then let’s say you want RxJava: [io.reactivex.rxjava2:rxjava:2.1.9]. Gradle checks for it in the Google repo, but Google responds with a HTTP 404 error, so Gradle moves on to the next repository, which is JCenter.  And on and on, for each dependency in your build.

This can lead to a couple of problems:

  • There’s a performance problem.  Since you have to check each repository in order for each dependency, there are a lot of requests that return a 404, and you waste time and resources.  Wouldn’t it be nice if we could tell Gradle “oh, I know RxJava is on JCenter, so don’t bother checking the Google repository”?
  • If a repository that’s first in the list gives a bad response (like the time JCenter responded to Google artifacts with an HTTP 409 error). Gradle will give up, and not check other repositories.  This will break your build, and leads to a lot of advice like “make sure you list the Google repository first!”
  • You’re vulnerable to a spoofing attack.  If you have a dependency that’s in your last repository (fabric in the example above), but a malicious actor uploads a library with the same group and artifact names to JCenter, for example, Gradle will download the JAR from JCenter since it’s higher in the list, and you’ll never know the difference.  JCenter doesn’t do much to verify that you are who you say you are, so this type of attack is a real risk.

So how do we resolve this?  Gradle 5.1 adds a new API so you can specify which groups to include or exclude in a repository.  A quick note about what I mean by groups: it’s the bit before the first colon in a Gradle coordinate.

So in practice, your build.gradle might now look something like this – note that you can match groups exactly or with a regular expression:

Now, when Gradle goes to download constraint layout, it will match the regex on the google repo, and Gradle will never attempt to download it from another repository.  Likewise, even though the Google maven repository is listed first, Gradle won’t attempt to download RxJava from it, because it’s not listed in the include groups.

If you want to test this, try running a Gradle task with “–refresh-dependencies”, which will force Gradle to try to download all of your dependencies again.  If you get an error like this one then you know you still need to work on your configuration.

> Could not resolve all files for configuration ':app:debugCompileClasspath'.
   > Could not find io.reactivex.rxjava2:rxjava:2.2.2.

The important thing to know about includes and excludes is that the behavior is defined per repository.

  • If you list an include – Gradle will only try to download the included groups for this repository.
  • If you list an exclude – Gradle will try any groups except the excluded groups for this repository.
  • If you list includes and excludes – Gradle will download only groups that are included and not excluded.

There’s one non-obvious thing that I want to really emphasize, though:  If you specify includeGroups on repo but don’t specify any groups on a second repo, like the screenshot below, Gradle will still try to download com.google artifacts from both of these repositories.  The options you declare for one repository don’t affect other repositories.

This leads to my last, but perhaps most important piece of advice: whitelist every group.  The best way I can see to use this is to include every group in the appropriate repository, and make sure every listed repository has an includeGroup declared.  This will force Gradle to download each dependency from the right repository only.

What’s the point of Lockdown Mode in Android P?

Lockdown Mode is a new feature in Android P — it disables fingerprint login, forcing you to enter your PIN/passcode to unlock the device.  This is an important, but subtle, distinction. Ultimately, what’s the difference between your passcode and your fingerprint?

The difference comes down to legal precedents in the US.  Legally, the police can compel you to unlock a device using biometrics (e.g. face or fingerprint).  They can’t, however, force you to unlock the device using your passcode. This is due to the Fifth Amendment to the US constitution — you can’t be compelled to testify against yourself.  Courts have said that revealing your passcode is equivalent to providing testimony against yourself — but using your body to unlock the phone isn’t legally the same thing. I’m not going to try to explain the underlying legal theories in depth, but if you want to read more, check out these articles:

There are a lot more details and complications that I’m glossing over, but if you were curious about why Android bothered to create a new mode that forced one type of login while disabling another — I think this is why they did it.

Disclaimer: I’m not a lawyer and this isn’t legal advice — if police action is in your threat model, you need to talk to a real lawyer.

Yes, you should run ProGuard / R8 on open-source library dependencies

I have seen a few people argue that there’s no reason to let ProGuard run on the open-source libraries that they include in their app, because if the source is publicly available, there’s no point of obfuscating, right? Who are you fooling? Here’s an example from one library’s README file:

Their ProGuard rules file is just a big wildcard keep:


I think that’s exactly wrong, for two reasons — shrinking, and obfuscation.

Shrinking

The majority of the shrinking that ProGuard performs is by removing unused code from third-party libraries. You’re not writing a lot of code that you don’t use, but you’re probably not using all of the features in the third-party libraries that you include. If you use a

-keep com.example.** { *; }

-type rule on open source libraries, you won’t get the benefits from ProGuard’s shrinking. None of that unused code will be removed.

Obfuscation

I’m going to show this with an illustration. What does this class do?

The eagle-eyed among you may recognize a couple of clues and have a hunch, but I doubt you know everything that it’s doing. So let’s look at the same class, but without obfuscating the third-party libraries.


It’s a lot easier to figure out what this code does when the libraries aren’t obfuscated. Simply put, obfuscating the open-source code allows for better obfuscation of your own your code.
(I mention ProGuard by name, because it’s still the main tool for Android development, but these arguments apply just the same to R8.)

Quick-and-dirty flat-file storage

Not every app needs cloud storage with a SQLite cache.

I’m going to let you in on a little secret that helps me prototype apps quickly.  Sometimes, when nobody’s looking, I just write my data to disk. I don’t build a cloud backend.  I don’t build a SQLite database. I just serialize my data to JSON and write it to files on the disk.  It’s really easy.

There are definitely some drawbacks to this approach, so if your head is about to light on fire with anger towards me, skip to the Caveats section below.

The code

Here’s what it takes; it’s just a couple of lines of Kotlin.  Say I’ve got a FooConfiguration object that I want to persist to disk.  It’s complicated enough that I don’t want to put it in SharedPreferences, so we’re going to write it to a file.

Step 1: We have a couple of dependencies to bring in.

Step 2: we serialize the object to JSON and write it to disk.

Step 3: When we need to read that data, we read from disk and deserialize from JSON.

That’s it.  Persistent storage, done.

Caveats

Like I mentioned at the beginning, this is something I do to get an app idea off the ground quickly.  When you go to scale this up, you’re going to run into drawbacks.

  • Sync & conflicts – If you try to sync this storage over a cloud file store (e.g. Dropbox, Google Drive, etc), or build some import/export functionality, you’re quickly going to run into this question — How do I handle conflicts? This is a hard problem, and the best advice is to build your storage model such that somebody else handles that complexity.
  • Data model evolution – If you change your data model, you need to ensure that it’s backwards-compatible, or else you’ll lose data from previously-stored objects.
  • Security – storing your data to local disk may not make sense, depending on the sensitivity of the data.  This is highly dependent on your specific needs, but keep it in mind.
  • Local-only – The only copy of the user’s data is on their device.  If you want to build any kind of social app where users interact with each other’s data, this isn’t going to get the job done.

My point here is, this isn’t a perfect solution, and it may be downright bad for your app.  Don’t take my advice blindly.

Ship It

I have multiple apps in the Play Store using this method right now.  No, I’m not going to tell you which ones. My point is, if you’re working on an app but you’re getting stuck on building a backend, this is a way to punt on the complexity of your persistent storage.  Build it quick-and-dirty for now, so you can focus on the app; revisit a more scalable solution for storage when you’re ready.