1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.virtualisation.anbox;
8 kernelPackages = config.boot.kernelPackages;
9 addrOpts = v: addr: pref: name: {
10 address = mkOption {
11 default = addr;
12 type = types.str;
13 description = ''
14 IPv${toString v} ${name} address.
15 '';
16 };
17
18 prefixLength = mkOption {
19 default = pref;
20 type = types.addCheck types.int (n: n >= 0 && n <= (if v == 4 then 32 else 128));
21 description = ''
22 Subnet mask of the ${name} address, specified as the number of
23 bits in the prefix (<literal>${if v == 4 then "24" else "64"}</literal>).
24 '';
25 };
26 };
27
28in
29
30{
31
32 options.virtualisation.anbox = {
33
34 enable = mkEnableOption "Anbox";
35
36 image = mkOption {
37 default = pkgs.anbox.image;
38 defaultText = literalExpression "pkgs.anbox.image";
39 type = types.package;
40 description = ''
41 Base android image for Anbox.
42 '';
43 };
44
45 extraInit = mkOption {
46 type = types.lines;
47 default = "";
48 description = ''
49 Extra shell commands to be run inside the container image during init.
50 '';
51 };
52
53 ipv4 = {
54 container = addrOpts 4 "192.168.250.2" 24 "Container";
55 gateway = addrOpts 4 "192.168.250.1" 24 "Host";
56
57 dns = mkOption {
58 default = "1.1.1.1";
59 type = types.str;
60 description = ''
61 Container DNS server.
62 '';
63 };
64 };
65 };
66
67 config = mkIf cfg.enable {
68
69 assertions = singleton {
70 assertion = versionAtLeast (getVersion config.boot.kernelPackages.kernel) "4.18";
71 message = "Anbox needs user namespace support to work properly";
72 };
73
74 environment.systemPackages = with pkgs; [ anbox ];
75
76 boot.kernelModules = [ "ashmem_linux" "binder_linux" ];
77 boot.extraModulePackages = [ kernelPackages.anbox ];
78
79 services.udev.extraRules = ''
80 KERNEL=="ashmem", NAME="%k", MODE="0666"
81 KERNEL=="binder*", NAME="%k", MODE="0666"
82 '';
83
84 virtualisation.lxc.enable = true;
85 networking.bridges.anbox0.interfaces = [];
86 networking.interfaces.anbox0.ipv4.addresses = [ cfg.ipv4.gateway ];
87
88 networking.nat = {
89 enable = true;
90 internalInterfaces = [ "anbox0" ];
91 };
92
93 systemd.services.anbox-container-manager = let
94 anboxloc = "/var/lib/anbox";
95 in {
96 description = "Anbox Container Management Daemon";
97
98 environment.XDG_RUNTIME_DIR="${anboxloc}";
99
100 wantedBy = [ "multi-user.target" ];
101 preStart = let
102 initsh = pkgs.writeText "nixos-init" (''
103 #!/system/bin/sh
104 setprop nixos.version ${config.system.nixos.version}
105
106 # we don't have radio
107 setprop ro.radio.noril yes
108 stop ril-daemon
109
110 # speed up boot
111 setprop debug.sf.nobootanimation 1
112 '' + cfg.extraInit);
113 initshloc = "${anboxloc}/rootfs-overlay/system/etc/init.goldfish.sh";
114 in ''
115 mkdir -p ${anboxloc}
116 mkdir -p $(dirname ${initshloc})
117 [ -f ${initshloc} ] && rm ${initshloc}
118 cp ${initsh} ${initshloc}
119 chown 100000:100000 ${initshloc}
120 chmod +x ${initshloc}
121 '';
122
123 serviceConfig = {
124 ExecStart = ''
125 ${pkgs.anbox}/bin/anbox container-manager \
126 --data-path=${anboxloc} \
127 --android-image=${cfg.image} \
128 --container-network-address=${cfg.ipv4.container.address} \
129 --container-network-gateway=${cfg.ipv4.gateway.address} \
130 --container-network-dns-servers=${cfg.ipv4.dns} \
131 --use-rootfs-overlay \
132 --privileged
133 '';
134 };
135 };
136 };
137
138}