about summary refs log tree commit diff
path: root/nixpkgs/pkgs/development/mobile/androidenv/emulate-app.nix
blob: 74e002bdd16f3cb2244961e3ac0e5b90551be66f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
{ composeAndroidPackages, stdenv, lib, runtimeShell }:
{ name, app ? null
, platformVersion ? "33"
, abiVersion ? "armeabi-v7a"
, systemImageType ? "default"
, enableGPU ? false
, extraAVDFiles ? []
, package ? null
, activity ? null
, androidUserHome ? null
, avdHomeDir ? null # Support old variable with non-standard naming!
, androidAvdHome ? avdHomeDir
, sdkExtraArgs ? {}
, androidAvdFlags ? null
, androidEmulatorFlags ? null
}:

let
  sdkArgs = {
    includeEmulator = true;
    includeSystemImages = true;
  } // sdkExtraArgs // {
    cmdLineToolsVersion = "8.0";
    platformVersions = [ platformVersion ];
    systemImageTypes = [ systemImageType ];
    abiVersions = [ abiVersion ];
  };

  sdk = (composeAndroidPackages sdkArgs).androidsdk;
in
stdenv.mkDerivation {
  inherit name;

  buildCommand = ''
    mkdir -p $out/bin

    cat > $out/bin/run-test-emulator << "EOF"
    #!${runtimeShell} -e

    # We need a TMPDIR
    if [ "$TMPDIR" = "" ]
    then
        export TMPDIR=/tmp
    fi

    ${if androidUserHome == null then ''
      # Store the virtual devices somewhere else, instead of polluting a user's HOME directory
      export ANDROID_USER_HOME=$(mktemp -d $TMPDIR/nix-android-user-home-XXXX)
    '' else ''
      mkdir -p "${androidUserHome}"
      export ANDROID_USER_HOME="${androidUserHome}"
    ''}

    ${if androidAvdHome == null then ''
      export ANDROID_AVD_HOME=$ANDROID_USER_HOME/avd
    '' else ''
      mkdir -p "${androidAvdHome}"
      export ANDROID_AVD_HOME="${androidAvdHome}"
    ''}

    # We need to specify the location of the Android SDK root folder
    export ANDROID_SDK_ROOT=${sdk}/libexec/android-sdk

    ${lib.optionalString (androidAvdFlags != null) ''
      # If NIX_ANDROID_AVD_FLAGS is empty
      if [[ -z "$NIX_ANDROID_AVD_FLAGS" ]]; then
        NIX_ANDROID_AVD_FLAGS="${androidAvdFlags}"
      fi
    ''}

    ${lib.optionalString (androidEmulatorFlags != null) ''
      # If NIX_ANDROID_EMULATOR_FLAGS is empty
      if [[ -z "$NIX_ANDROID_EMULATOR_FLAGS" ]]; then
        NIX_ANDROID_EMULATOR_FLAGS="${androidEmulatorFlags}"
      fi
    ''}

    # We have to look for a free TCP port

    echo "Looking for a free TCP port in range 5554-5584" >&2

    for i in $(seq 5554 2 5584)
    do
        if [ -z "$(${sdk}/bin/adb devices | grep emulator-$i)" ]
        then
            port=$i
            break
        fi
    done

    if [ -z "$port" ]
    then
        echo "Unfortunately, the emulator port space is exhausted!" >&2
        exit 1
    else
        echo "We have a free TCP port: $port" >&2
    fi

    export ANDROID_SERIAL="emulator-$port"

    # Create a virtual android device for testing if it does not exist
    if [ "$(${sdk}/bin/avdmanager list avd | grep 'Name: device')" = "" ]
    then
        # Create a virtual android device
        yes "" | ${sdk}/bin/avdmanager create avd --force -n device -k "system-images;android-${platformVersion};${systemImageType};${abiVersion}" -p $ANDROID_AVD_HOME $NIX_ANDROID_AVD_FLAGS

        ${lib.optionalString enableGPU ''
          # Enable GPU acceleration
          echo "hw.gpu.enabled=yes" >> $ANDROID_AVD_HOME/device.avd/config.ini
        ''}

        ${lib.concatMapStrings (extraAVDFile: ''
          ln -sf ${extraAVDFile} $ANDROID_AVD_HOME/device.avd
        '') extraAVDFiles}
    fi

    # Launch the emulator
    echo "\nLaunch the emulator"
    $ANDROID_SDK_ROOT/emulator/emulator -avd device -no-boot-anim -port $port $NIX_ANDROID_EMULATOR_FLAGS &

    # Wait until the device has completely booted
    echo "Waiting until the emulator has booted the device and the package manager is ready..." >&2

    ${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port wait-for-device

    echo "Device state has been reached" >&2

    while [ -z "$(${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port shell getprop dev.bootcomplete | grep 1)" ]
    do
        sleep 5
    done

    echo "dev.bootcomplete property is 1" >&2

    #while [ -z "$(${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port shell getprop sys.boot_completed | grep 1)" ]
    #do
        #sleep 5
    #done

    #echo "sys.boot_completed property is 1" >&2

    echo "ready" >&2

    ${lib.optionalString (app != null) ''
      # Install the App through the debugger, if it has not been installed yet

      if [ -z "${package}" ] || [ "$(${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port shell pm list packages | grep package:${package})" = "" ]
      then
          if [ -d "${app}" ]
          then
              appPath="$(echo ${app}/*.apk)"
          else
              appPath="${app}"
          fi

          ${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port install "$appPath"
      fi

      # Start the application
      ${lib.optionalString (package != null && activity != null) ''
          ${sdk}/libexec/android-sdk/platform-tools/adb -s emulator-$port shell am start -a android.intent.action.MAIN -n ${package}/${activity}
      ''}
    ''}
    EOF
    chmod +x $out/bin/run-test-emulator
  '';
}