Least-Complex Solution

Picking the right tool for the job is always hard, and it’s made harder even by the sheer amount of really bad tooling. A story how how to replace 3.8kloc with 50.

Bad tools are everywhere and have one or more of the following attributes:

I recently found myself with a new corporate-issued Samsung device, full of low-utility tools like Bixby, OneUI, and Samsung copies of Google services. Most of these packages can be uninstalled using ADP (the Android Debug Bridge) so I found an OSS project - a piece of software that scans the packages on your phone, gives you recommendations on what packages you could remove, and removes them for you.

As it happens, it’s also a Rust tool, and I do like Rust so I attempt to install the binary they provide in the GitHub artifact - which does not work. Fair enough - I’m not one of those snobby OSS users who then turn around and scream at the maintainers for providing a ‘broken’ binary - I check out the source code and build it myself. I failed at this too - probably b/c I’m on an Apple Silicon Macbook Pro - fair.

But that got me thinking - what does this tool do? It’s quite simple:

  1. Read a JSON file that has all known ‘bloatware’ packages from many smartphone manufacturers
  2. Loads a list of installed packages from the phone through adb
  3. Cross-references the package list from the phone with the bloatware list
  4. Uninstalles the matching packages through adb

The complexity of the project I found was I building a cross-platform GUI - the actual logic to uninstall packages is trivial. So I resorted to my favorite scripting language (F#) and built it myself:

Let’s start by pulling in a very useful library called fli as it will make our lives a LOT simpler in just a minute:

0
1
2
3
4
5
#r "nuget: Fli, 1.111.10"

open Fli
open System.Text.Json
open System.Text.Json.Serialization
open System.IO

Next, we download the JSON assets from the original OSS project which contains the bloatware list, and deserialize the JSON:

 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let asset_download = 
    cli {
        Exec "curl"
        Arguments ["https://raw.githubusercontent.com/0x192/universal-android-debloater/main/resources/assets/uad_lists.json"]
    }
    |> Command.execute

type Removal = {
    id: string
    list: string
    description: string
    removal: string
}

let removals = JsonSerializer.Deserialize<Removal seq>(asset_download.Text.Value)

We can already see: that there is no need for fancy http libraries etc. Instead we simply use the unix tools already present on our machines and call curl to get the content of the file.

Next we’ll fetch the installed packages using adb shell cmd package list and do some trimming and cross-reference with the pkgs which contains the list of bloatware packages.

25
26
27
28
29
30
31
32
33
34
35
36
37
38
let installed_packages_cmd = 
    cli {
        Exec "adb"
        Arguments ["shell" ; "cmd" ; "package" ; "list" ; "package"]
    }
    |> Command.execute
let pkgs = 
    installed_packages_cmd.Text.Value.Split('\n')
    |> Seq.map (fun x -> x.[8..])
    |> Set.ofSeq
let packages_to_remove = 
    removals
    |> Seq.filter (fun x -> pkgs.Contains(x.id) && x.removal = "Recommended")
    |> Seq.map (fun x -> x.id)

All that’s left now, is to use adb uninstall to remove thos packages and we’re done:

39
40
41
42
43
44
45
46
for p in packages_to_remove do
    printfn "Uninstalling: %s" (Path.GetFileName(p))
    cli {
        Exec "adb"
        Arguments ["uninstall"; p ]
    }
    |> Command.execute
    |> ignore

Just by having the skill of being able to write a simple script in F# (yes, I am aware this could all have been done in Bash, JS/Node and the snake-language) I could cut out much of the complexity of original project (Universal Android Debloater) and do the same thing with less than 50 loc (with room for even more simplification and reduction) and in under 2 hours. This is NOT a criticism of the original project or their maintainers and I am well aware of why they opted to build a UI as their target audience does not have scripting skills.

Too often, however, do we build tools for computing engineers - all of whom SHOULD be able to either write or at the very least run, a simple script - as if they were technically illiterate.

Go for the least-complex solution and stop there.