Sunday, June 14, 2015

Blade Reminder App for Android

In recent years the traditional double-edged safety razor has been gaining popularity.



I've also joined the ever-growing fanbase of safety razors. And to help me keep track of when I last changed my razor blade I've created this Android app called Blade Reminder. The app is pretty simple, showing a list of dates, number of times the blade's been used, and a comment (in case I forget how well that day's face scraping went).



So what's the appeal of the safety razor over more modern inventions? I think most converts would agree that a big factor is the cost. Multi-blade cartridge razors feel like such a rip off in comparison.
  • DE blades cost less than 15€ for 100 (online)
  • Even offline in the supermarket they cost less than 3€ for 10
  • Compare that to the Mach 3, which costs 20€ or more for an 8 pack of blades
  • The initial outlay for a DE razor is more, but it's a sturdier piece of kit
You could make those Mach 3s last a couple of years too, if you don't mind shaving with a dull instrument most of the time.

Cost isn't the only factor of course, since if you want to spend more then a good razor and badger brush can be pricey. I think that once you get the technique down, the safety razor provides a shave that is comparable to a cartridge, but is more comfortable to perform.

Yes, there is a definite learning curve. This difficulty could be another paradoxical reason for the increased popularity of the safety razor. Requiring effort to use something makes people feel like they are not such failures when they finally do gain the necessary skill. It's the "I like using this because it makes me feel less of a n00b"-effect. This article talks about the phenomenon of popular products that require effort to use.

Knowing when to change the blade in a DE razor is something that you have to learn. It tends to take about a week of daily use before a used blade starts tugging at hairs rather than slicing through them. Of course the duration of a blade depends on a lot of factors, such as how stubbly you are and the make of blades, but 5 uses seems to be about average.

At the point the blade starts to dull, one tends to instinctively press a fraction harder. This extra pressure ends up causing nicks and razor burn. By keeping track of the number of times you've used the blade, you get to know when you need to switch it out. Or at least it makes you think about those things.

That's where this app comes in! I started making this months ago, but I it has taken me this long to get round to releasing it. As well as the main spreadsheet-like application I've also added a widget that shows the usage and date from the last time you used the app.



There's another app on the Play store called "Shaving Buddy" that does something similar actually. However it has a lot more features than I needed, with photos and databases of gear and all sorts of bells and whistles. If you prefer that approach, maybe check that app out too.

Creating this app has made me realise just how much effort writing a proper Android app requires. Compared to a game, I mean, which has its own strengths and weaknesses. The tools and the libraries in the Android world have a lot of churn. Google is notorious for throwing new libraries out and then abandoning the old working ones for these new-but-incomplete versions. All the ancient documentation that's out there on blogs and tutorials causes confusion.

Backwards-compatibility cruft is starting to accumulate too, such as the official android command-line tool that still only creates projects for the old deprecated apache-ant-based build system. It's a miracle anyone writes apps actually. The time required to do anything useful, let alone fancy, is overwhelming. Even once it is done, I go back to code and struggle to remember what it all oes (ContentObservers, ContentResolvers, Fragments, Cursors, Loaders, Adapters… argh!). Maybe I just suck at this.

According to these guys, it takes 18 weeks to write an Android app. That includes server-side logic, "monetization strategies", and other complicated stuff I don't do. In my defence, I wrote this app at night and on weekends between bouts of Monster Hunter 3 Ultimate with my son. I didn't see any mention of Defeating Brachydios on that infographic.



Luckily we have a bunch of static analysis tools for Java to help prevent common mistakes, and the Android lint tool helps you avoid incorrect use of the dodgier parts of the Android API. In general writing an app "only" requires bolting together different libraries and functions until it does what you want. When I found myself having to write too much code it either meant I was doing it wrong, or I just shouldn't be doing it at all. The downside to this is that whatever you write today, the next platform version could make obsolete or incompatible tomorrow.

Speaking of obsolete, maintaining backwards compatibility with the old 2.x or 3.x versions of Android seems pretty pointless nowadays. About 85% of the people with Android devices have them running 4.0 and up, and this percentage will only continue to grow. For my old 8-bit remakes I've kept backwards compatibility because when I started, Android Donut (aka 1.6) was still cutting edge. The native C code APIs in Android have not changed at all in the last 5-6 years. The lesson here is to write everything in C, it is future proof! :)

Initially I planned to not use the support libraries, but this meant not taking advantage of back-ported functions from the Lollipop release. I also worry that by sticking with the older code style - ActionBar and pals - the code will become stale once 5.x starts to get a foothold.

A feature I have considered adding to the app was an online backup of some kind. Right now I've included a simple backup option where you can export a CSV file of the data to email to yourself or store on some remote storage service. Aftewards you can re-import that file if you lose the data or want to update the entries on a new device. This doesn't require permissions of any sort since it uses Android Intents. It'd be better to use Google Drive, Dropbox or Owncloud, but this would all require more work as well as requiring the internet privilege, so I've avoided doing that for now.

Here's that link to the app on the play store again, try it out and rate it. The code is also on github, so get those pull requests rolling in.

Sunday, May 31, 2015

Fixing bugs with GCC's -fsanitize

New versions of gcc have added several options to check for undefined behaviour, out-of-bounds behaviour, and other run-time bugs in a program.

To use address sanitizer, you have to compile and link your code with the -fsanitize=address option. Then run your program as normal. If there are any buggy bits, then the compiler-inserted runtime checks will throw a wobbly.

Using the CMake build system I add the flag like this:

include(CheckCCompilerFlag)
set(flag fsanitize=address)
set(CMAKE_REQUIRED_FLAGS -${flag})
check_c_compiler_flag(-${flag} ${flag}_is_ok)
if(${flag}_is_ok)
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -${flag}")
endif()

I had to set CMAKE_REQUIRED_FLAGS as otherwise the check_c_compiler_flag macro just uses -fsanitize on the compile stage, skips it on the link stage, and the check subsequently fails with an undefined reference:

CMakeFiles/cmTryCompileExec3487553727.dir/src.c.o: In function `_GLOBAL__sub_I_00099_0_main':
src.c:(.text+0x10): undefined reference to `__asan_init_v4'
collect2: error: ld returned 1 exit status

Anyway… for an out-of-bounds read, a crash looked like this (I compiled in Debug to get line information):

=================================================================
==16223==ERROR: AddressSanitizer: global-buffer-overflow on address 0x0000004833bc at pc 0x000000412a7a bp 0x7ffe5fb416e0 sp 0x7ffe5fb416d0
READ of size 2 at 0x0000004833bc thread T0
    #0 0x412a79 in load_bg_palette chaos/gfx.c:306
    #1 0x412824 in load_all_palettes chaos/gfx.c:292
    #2 0x415d8d in load_bg chaos/main.c:23
    #3 0x415e4c in chaos_main chaos/main.c:37
    #4 0x407758 in main port/linux/main.c:52
    #5 0x7f6c1551778f in __libc_start_main (/usr/lib/libc.so.6+0x2078f)
    #6 0x406e08 in _start (build/linux-cmake/port/linux/chaos+0x406e08)

0x0000004833bc is located 0 bytes to the right of global variable 'palette1Pal' defined in 'gfx/palette1/palette1.c:16:22' (0x4833a0) of size 28
SUMMARY: AddressSanitizer: global-buffer-overflow chaos/gfx.c:306 load_bg_palette
Shadow bytes around the buggy address:
  0x000080088620: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x000080088630: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x000080088640: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x000080088650: 00 00 00 00 00 00 00 00 f9 f9 f9 f9 00 00 00 00
  0x000080088660: 00 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 00 00 00 00
⇾0x000080088670: 00 00 00 00 00 00 00[04]f9 f9 f9 f9 00 00 00 00
  0x000080088680: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x000080088690: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800886a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0000800886b0: f9 f9 f9 f9 00 00 00 00 00 f9 f9 f9 f9 f9 f9 f9
  0x0000800886c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
==16223==ABORTING

Chaos has run "valgrind clean" for a while, which means that there should be no out-of-bounds writes. The nice thing about address sanitizer is that it seems to find extra problematic areas over valgrind, and the runtime overhead is less. I've noticed that buggy code that does out-of-bounds reads on an x86 machine tends to work fine, but when you do the same on Android running on an ARM processor it crashes right away.

Hopefully armed with this new tool I'll be able to fix more bugs :-D

Tuesday, May 26, 2015

Chaos update notes

A few weeks ago I released a new version of Chaos that had what looked to be some pretty bad bugs. In fact the bug was just that the controls were enabled when the CPU is moving or casting, but you could really mess things up by tapping the screen during their turns.

This bug came about because I'd switched over to the latest emscripten build for the Javascript version.

As you may recall, I had to make some changes to the code structure of Chaos to avoid busy-loops deep in the guts of the game. Emscripten previously required you to have just one "main loop". Since then, Emscripten has gone completely insane - in a good way. The latest emscripten releases have a way to compile C code down to an emscripten-specific byte-code, that is then run on an interpreter, which itself runs in the Javascript interpreter in the browser!

What this marvellous change means is that the one-main-loop restriction is lifted. You get better performance if you write code to use the preferred way, but the interpreter-byte-code solution lets you keep the old semantics going if you don't mind taking the performance hit. Hurray! So I reverted Chaos back to how it was in release 1.18 more or less. That means no more co-routines to get around the loop restriction, and the code is easier to read and understand.

As a side effect, I added the previously-mentioned game destroying bug by enabling controls in the "wait" code that was called everywhere. The fix was easy - just remove the control polling/updates in the wait. But it broke the game for a lot of people :( To try and prevent this from happening again, I've created a beta group on Google+ and I'll push updates here before making them live to everyone.

I currently have 1 bug on my list - fix the glitched screen that happens after you minimize the game and return to the homescreen, then go back to the game. I'm doing something wrong in the Android lifecycle and the GL screen is not reset correctly.