Skip to content
This repository

[fix] Emulator preloading, added library version #166

Closed
wants to merge 1 commit into from

2 participants

François-Xavier Thomas Matthew Willis
François-Xavier Thomas

This is a fix for emulator preloading, now works without any issue, at least on my side :

> sbt
Loading project definition from /Users/fx/Documents/Projects/Lunar/project
[info] Set current project to Lunar (in build file:/Users/fx/Documents/Projects/Lunar/)

> android:preload-emulator Galaxy_Nexus_HAXM
[info] Starting emulator with read-write system
[info] This may take a while...
Warning: No DNS servers found
HAX is working and emulator runs in fast virt mode
[info] Installing scala-library.2.10.0.jar
[info] Setting permissions for scala-library.2.10.0.jar
[success] Total time: 31 s, completed Mar 15, 2013 10:52:20 PM

> android:emulator-start Galaxy_Nexus_HAXM
[success] Total time: 0 s, completed Mar 15, 2013 10:52:31 PM

> android:start-emulator
[info] Wrote /Users/fx/Documents/Projects/Lunar/target/scala-2.10/src_managed/main/scala/com/github/fxthomas/lunar/TR.scala
[info] Skipping Proguard
[info] Packaging /Users/fx/Documents/Projects/Lunar/target/lunar-0.1.apk
[success] Total time: 4 s, completed Mar 15, 2013 10:53:35 PM
Matthew Willis
Collaborator

Really awesome work. I did notice some problems when I already had the emulator open, but otherwise it worked perfectly!

Also, I made a patch to standardize the file and permission names to a format like "scala-library-2.10.0.jar". That seems less confusing for users who otherwise would need to cat the permissions xml file to get the permission name.

Matthew Willis appamatto closed this March 17, 2013
Matthew Willis
Collaborator

Forgot to note that this has been merged. Thanks!

François-Xavier Thomas

Excellent! Glad it worked!

By the way, if the emulator is already started, the command kills it by default. I'm not sure whether this is required, but there will probably be conflicts between the read-only and the read-write emulators if I don't do that, so...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Mar 15, 2013
François-Xavier Thomas [fix] Emulator preloading, added library version 363720c
This page is out of date. Refresh to see the latest.
1  src/main/scala/Android.scala
@@ -48,6 +48,7 @@ object AndroidProject extends Plugin {
48 48
 
49 49
   lazy val androidSettings: Seq[Setting[_]] =
50 50
     AndroidBase.settings ++
  51
+    AndroidPreload.settings ++
51 52
     AndroidLaunch.settings ++
52 53
     AndroidDdm.settings
53 54
 
9  src/main/scala/AndroidHelpers.scala
@@ -24,14 +24,19 @@ object AndroidHelpers {
24 24
   def usesSdk(mpath: File, schema: String, key: String) =
25 25
     (manifest(mpath) \ "uses-sdk").head.attribute(schema, key).map(_.text.toInt)
26 26
 
27  
-  def adbTask(dPath: String, emulator: Boolean, s: TaskStreams, action: String*) {
  27
+  def adbTask(dPath: String, emulator: Boolean, s: TaskStreams, action: String*) = {
28 28
     val (exit, out) = adbTaskWithOutput(dPath, emulator, s, action:_*)
  29
+
  30
+    // Check exit value
29 31
     if (exit != 0 ||
30 32
         // adb doesn't bother returning a non-zero exit code on failure
31 33
         out.toString.contains("Failure")) {
32 34
       s.log.error(out.toString)
33 35
       sys.error("error executing adb")
34  
-    } else s.log.info(out.toString)
  36
+    } else s.log.debug(out.toString)
  37
+
  38
+    // Return output
  39
+    out.toString
35 40
   }
36 41
 
37 42
   def adbTaskWithOutput(dPath: String, emulator: Boolean, s: TaskStreams, action: String*) = {
2  src/main/scala/AndroidInstall.scala
@@ -12,10 +12,12 @@ object AndroidInstall {
12 12
 
13 13
   private def installTask(emulator: Boolean) = (dbPath, packageApkPath, streams) map { (dp, p, s) =>
14 14
     adbTask(dp.absolutePath, emulator, s, "install", "-r ", p.absolutePath)
  15
+    ()
15 16
   }
16 17
 
17 18
   private def uninstallTask(emulator: Boolean) = (dbPath, manifestPackage, streams) map { (dp, m, s) =>
18 19
     adbTask(dp.absolutePath, emulator, s, "uninstall", m)
  20
+    ()
19 21
   }
20 22
 
21 23
   private def aaptPackageTask: Project.Initialize[Task[File]] =
6  src/main/scala/AndroidKeys.scala
@@ -201,14 +201,12 @@ object AndroidKeys {
201 201
   val remountEmulator = TaskKey[Unit]("remount-emulator")
202 202
 
203 203
   /** Install Scala on device/emulator **/
204  
-  val preloadedDevice   = TaskKey[Option[String]]("preloaded-device", "The version of Scala currently on the device")
205  
-  val preloadedEmulator = TaskKey[Option[String]]("preloaded-emulator", "The version of Scala currently on the emulator")
206 204
   val preloadDevice     = TaskKey[Unit]("preload-device", "Setup device for development by uploading the predexed Scala library")
207  
-  val preloadEmulator   = TaskKey[Unit]("preload-emulator", "Setup emulator for development by uploading the predexed Scala library")
  205
+  val preloadEmulator   = InputKey[Unit]("preload-emulator", "Setup emulator for development by uploading the predexed Scala library")
208 206
 
209 207
   /** Unload Scala from device/emulator **/
210 208
   val unloadDevice   = TaskKey[Unit]("unload-device", "Unloads the Scala library from the device")
211  
-  val unloadEmulator = TaskKey[Unit]("unload-emulator", "Unloads the Scala library from the emulator")
  209
+  val unloadEmulator = InputKey[Unit]("unload-emulator", "Unloads the Scala library from the emulator")
212 210
 
213 211
   /** Use preloaded Scala for development **/
214 212
   val usePreloadedScala = SettingKey[Boolean]("use-preloaded-scala",
1  src/main/scala/AndroidLaunch.scala
@@ -13,6 +13,7 @@ object AndroidLaunch {
13 13
               emulator, s,
14 14
               "shell", "am", "start", "-a", "android.intent.action.MAIN",
15 15
               "-n", mPackage+"/"+launcherActivity(schema, amPath.head, mPackage))
  16
+      ()
16 17
   }
17 18
 
18 19
   private def launcherActivity(schema: String, amPath: File, mPackage: String) = {
237  src/main/scala/AndroidPreload.scala
@@ -11,16 +11,12 @@ object AndroidPreload {
11 11
   private def deviceJarPath(lib: File, ver: String) =
12 12
     "/system/framework/" + jarName(lib,ver)
13 13
 
  14
+  private def devicePermissionPath(ver: String) =
  15
+    "/system/etc/permissions/scala-library." + ver + ".xml"
  16
+
14 17
   private def deviceDesignation(implicit emulator: Boolean) =
15 18
     if (emulator) "emulator" else "device"
16 19
 
17  
-  private def deviceTask[T]
18  
-    (task_device: TaskKey[T], task_emulator: TaskKey[T])(implicit emulator: Boolean) =
19  
-    if (emulator) task_emulator else task_device
20  
-
21  
-  private def preloaded(implicit emulator: Boolean) =
22  
-    deviceTask(preloadedDevice, preloadedEmulator)
23  
-
24 20
   /****************
25 21
    * State checks *
26 22
    ****************/
@@ -28,13 +24,11 @@ object AndroidPreload {
28 24
   private def checkFileExists (db: File, s: TaskStreams, filename: String)(implicit emulator: Boolean) = {
29 25
 
30 26
     // Run the `ls` command on the device/emulator
31  
-    val fileR = adbTaskWithOutput(db.absolutePath, emulator, s,
  27
+    val flist = adbTask(db.absolutePath, emulator, s,
32 28
       "shell", "ls", filename, "2>/dev/null")
33  
-    val fileE = fileR._1
34  
-    val fileS = fileR._2
35 29
 
36 30
     // Check if we found the file
37  
-    val found = fileE == 0 && fileS.contains(filename)
  31
+    val found = flist.contains(filename)
38 32
 
39 33
     // Inform the user
40 34
     s.log.debug ("File " + filename +
@@ -47,16 +41,13 @@ object AndroidPreload {
47 41
   private def checkPreloadedScalaVersion (db: File, si: ScalaInstance, s: TaskStreams)(implicit emulator: Boolean) = {
48 42
     import scala.xml._
49 43
 
50  
-    // Wait for the device
51  
-    doWaitForDevice(db, s)
52  
-
53 44
     // Retrieve the contents of the `scala_library` permission file
54  
-    val permissions = adbTaskWithOutput(db.absolutePath, emulator, s,
55  
-      "shell", "cat /system/etc/permissions/scala_library.xml")
  45
+    val permissions = adbTask(db.absolutePath, emulator, s,
  46
+      "shell", "cat /system/etc/permissions/scala-library." + si.version + ".xml")
56 47
 
57 48
     // Parse the library file
58 49
     val preloadedScalaFile = (
59  
-      try { Some(XML.loadString(permissions._2) \\ "permissions" \\ "library" \\ "@file") }
  50
+      try { Some(XML.loadString(permissions) \\ "permissions" \\ "library" \\ "@file") }
60 51
       catch { case _ => None }
61 52
 
62 53
     // Convert the XML node to a String
@@ -83,8 +74,8 @@ object AndroidPreload {
83 74
    * Scala preloading process *
84 75
    ****************************/
85 76
 
86  
-  private def doPreloadPermissions (
87  
-    db: File, si: ScalaInstance, s: TaskStreams)(implicit emulator: Boolean) = {
  77
+  private def doPreloadPermissions(
  78
+    db: File, si: ScalaInstance, s: TaskStreams)(implicit emulator: Boolean): Unit = {
88 79
 
89 80
     // Inform the user
90 81
     s.log.info("Setting permissions for "
@@ -94,7 +85,7 @@ object AndroidPreload {
94 85
     val xmlContent =
95 86
       <permissions>
96 87
         <library
97  
-        name="scala_library"
  88
+        name={{ "scala_library_" + si.version }}
98 89
         file={{ deviceJarPath(si.libraryJar, si.version) }} />
99 90
       </permissions>
100 91
 
@@ -105,16 +96,14 @@ object AndroidPreload {
105 96
     ).toString.replace("\"", "\\\"")
106 97
 
107 98
     // Load the file on the device
108  
-    adbTaskWithOutput (db.absolutePath, emulator, s,
  99
+    adbTask (db.absolutePath, emulator, s,
109 100
       "shell", "echo", xmlString,
110  
-      ">", "/system/etc/permissions/scala_library.xml"
111  
-
112  
-    // Return true on success
113  
-    )._1 == 0
  101
+      ">", devicePermissionPath(si.version)
  102
+    )
114 103
   }
115 104
 
116  
-  private def doPreloadJar (
117  
-    db: File, dx: File, target: File, si: ScalaInstance, s: TaskStreams)(implicit emulator: Boolean) = {
  105
+  private def doPreloadJar(
  106
+    db: File, dx: File, target: File, si: ScalaInstance, s: TaskStreams)(implicit emulator: Boolean): Unit = {
118 107
 
119 108
     // This is the temporary JAR path
120 109
     val name = jarName(si.libraryJar, si.version)
@@ -138,65 +127,183 @@ object AndroidPreload {
138 127
 
139 128
     // Load the file on the device
140 129
     s.log.info("Installing " + name)
141  
-    adbTaskWithOutput (db.absolutePath, emulator, s,
  130
+    adbTask (db.absolutePath, emulator, s,
142 131
       "push",
143 132
       tempJarPath.getAbsolutePath,
144 133
       deviceJarPath(si.libraryJar, si.version)
  134
+    )
  135
+  }
145 136
 
146  
-    // Return true on success
147  
-    )._1 == 0
  137
+  private def doReboot (db: File, s: TaskStreams)(implicit emulator: Boolean) = {
  138
+      s.log.info("Rebooting " + deviceDesignation)
  139
+      if (emulator) adbTask(db.absolutePath, emulator, s, "emu", "kill")
  140
+      else adbTask(db.absolutePath, emulator, s, "reboot")
  141
+      ()
148 142
   }
149 143
 
150  
-  private def doWaitForDevice (db: File, s: TaskStreams)(implicit emulator: Boolean) = {
151  
-    s.log.info("Waiting for " + deviceDesignation)
152  
-    adbTaskWithOutput(db.absolutePath, emulator, s, "wait-for-device")
153  
-    ()
  144
+  private def doRemountReadWrite (db: File, s: TaskStreams)(implicit emulator: Boolean) = {
  145
+    s.log.info("Remounting /system as read-write")
  146
+    adbTask(db.absolutePath, emulator, s, "root")
  147
+    adbTask(db.absolutePath, emulator, s, "wait-for-device")
  148
+    adbTask(db.absolutePath, emulator, s, "remount")
154 149
   }
155 150
 
156  
-  private def doReboot (db: File, s: TaskStreams)(implicit emulator: Boolean) = {
157  
-    s.log.info("Rebooting " + deviceDesignation)
158  
-    adbTaskWithOutput(db.absolutePath, emulator, s, "reboot")
159  
-    ()
  151
+  /***************************
  152
+   * Emulator-specific stuff *
  153
+   ***************************/
  154
+
  155
+  private def doStartEmuReadWrite (db: File, s: TaskStreams,
  156
+    sdkPath: File, toolsPath: File, avdName: String, verbose: Boolean) = {
  157
+
  158
+    // Find emulator config path
  159
+    val avdPath = Path.userHome / ".android" / "avd" / (avdName + ".avd")
  160
+
  161
+    // Open config.ini
  162
+    val configFile = avdPath / "config.ini"
  163
+
  164
+    // Read the contents and split by newline
  165
+    val configContents = scala.io.Source.fromFile(configFile).mkString
  166
+
  167
+    // Regexp to match the system dir
  168
+    val sysre = "image.sysdir.1 *= *(.*)".r 
  169
+
  170
+    sysre findFirstIn configContents match {
  171
+      case Some(sysre(sys)) =>
  172
+
  173
+        // Copy system image to the emulator directory if needed
  174
+        val rosystem = sdkPath / sys / "system.img"
  175
+        val rwsystem = avdPath / "system.img"
  176
+        if (!rwsystem.exists) {
  177
+          s.log.info("Copying system image")
  178
+          "cp %s %s".format(rosystem.getAbsolutePath, rwsystem.getAbsolutePath).!
  179
+        }
  180
+
  181
+        // Start the emulator with the local persistent system image
  182
+        s.log.info("Starting emulator with read-write system")
  183
+        s.log.info("This may take a while...")
  184
+
  185
+        val rwemuCmdF = "%s/emulator -avd %s -no-boot-anim -no-snapshot -qemu -nand system,size=0x1f400000,file=%s -nographic -monitor null"
  186
+        val rwemuCmdV = "%s/emulator -avd %s -no-boot-anim -no-snapshot -verbose -qemu -nand system,size=0x1f400000,file=%s -nographic -show-kernel -monitor null"
  187
+        val rwemuCmd = (if (!verbose) rwemuCmdF else rwemuCmdV)
  188
+          .format(toolsPath, avdName, (avdPath / "system.img").getAbsolutePath)
  189
+
  190
+        s.log.debug (rwemuCmd)
  191
+        rwemuCmd.run
  192
+
  193
+        // Remount system as read-write
  194
+        adbTask(db.absolutePath, true, s, "wait-for-device")
  195
+        adbTask(db.absolutePath, true, s, "root")
  196
+        adbTask(db.absolutePath, true, s, "wait-for-device")
  197
+        adbTask(db.absolutePath, true, s, "remount")
  198
+
  199
+      case None => throw new Exception("Unable to find the system image")
  200
+    }
160 201
   }
161 202
 
  203
+  private def doKillEmu (db: File, s: TaskStreams)(implicit emulator: Boolean) = {
  204
+      if (emulator)
  205
+        adbTaskWithOutput(db.absolutePath, emulator, s, "emu", "kill")
  206
+      ()
  207
+  }
  208
+
  209
+
162 210
   /*******************************
163 211
    * Tasks related to preloading *
164 212
    *******************************/
165 213
 
166  
-  private def preloadedTask(implicit emulator: Boolean) =
167  
-    (dbPath, scalaInstance, streams) map (checkPreloadedScalaVersion _)
  214
+  private def preloadDeviceTask =
  215
+    (dbPath, dxPath, target, scalaInstance, streams) map {
  216
+    (dbPath, dxPath, target, scalaInstance, streams) =>
  217
+
  218
+      // We're not using the emulator
  219
+      implicit val emulator = false
  220
+
  221
+      // Wait for the device
  222
+      adbTask(dbPath.absolutePath, emulator, streams, "wait-for-device")
168 223
 
169  
-  private def preloadTask(implicit emulator: Boolean) =
170  
-    (preloaded, dbPath, dxPath, target, scalaInstance, streams) map {
171  
-    (preloaded, dbPath, dxPath, target, scalaInstance, streams) =>
  224
+      // Check if the Scala library is already prelaoded
  225
+      checkPreloadedScalaVersion(dbPath, scalaInstance, streams) match {
172 226
 
173  
-      preloaded match {
174 227
         // Don't do anything if the library is preloaded
175 228
         case Some(_) => ()
176 229
 
177 230
         // Preload the Scala library
178 231
         case None =>
  232
+          // Remount the device in read-write mode
  233
+          doRemountReadWrite (dbPath, streams)
  234
+
179 235
           // Push files to the device
180  
-          if (
181  
-            doPreloadJar         (dbPath, dxPath, target, scalaInstance, streams) &&
182  
-            doPreloadPermissions (dbPath, scalaInstance, streams)
  236
+          doPreloadJar         (dbPath, dxPath, target, scalaInstance, streams)
  237
+          doPreloadPermissions (dbPath, scalaInstance, streams)
183 238
 
184  
-          // Reboot once this is done
185  
-          ) doReboot(dbPath, streams)
  239
+          // Reboot / Kill emulator
  240
+          doReboot (dbPath, streams); ()
186 241
       }
187 242
     }
188 243
 
  244
+  private def preloadEmulatorTask(emulatorName: TaskKey[String]) =
  245
+    (emulatorName, toolsPath, sdkPath, dbPath, dxPath, target, scalaInstance, streams) map {
  246
+    (emulatorName, toolsPath, sdkPath, dbPath, dxPath, target, scalaInstance, streams) =>
  247
+
  248
+      // We're using the emulator
  249
+      implicit val emulator = true
  250
+
  251
+      // Kill any running emulator
  252
+      doKillEmu (dbPath, streams)
  253
+
  254
+      // Restart the emulator in system read-write mode
  255
+      doStartEmuReadWrite (dbPath, streams, sdkPath, toolsPath, emulatorName, false)
  256
+
  257
+      // Push files to the device
  258
+      doPreloadJar         (dbPath, dxPath, target, scalaInstance, streams)
  259
+      doPreloadPermissions (dbPath, scalaInstance, streams)
  260
+
  261
+      // Reboot / Kill emulator
  262
+      doKillEmu (dbPath, streams); ()
  263
+    }
  264
+
189 265
   private def commandTask(command: String)(implicit emulator: Boolean) =
190 266
     (dbPath, streams) map {
191  
-      (d,s) => adbTaskWithOutput(d.absolutePath, emulator, s, command)
  267
+      (d,s) => adbTask(d.absolutePath, emulator, s, command)
192 268
       ()
193 269
     }
194 270
 
195  
-  private def unloadTask(implicit emulator: Boolean) =
  271
+  private def unloadDeviceTask =
196 272
     (dbPath, scalaInstance, streams) map {
197 273
       (d,si,s) =>
198  
-        adbTaskWithOutput(d.absolutePath, emulator, s, "shell", "rm", deviceJarPath(si.libraryJar, si.version))
199  
-        adbTaskWithOutput(d.absolutePath, emulator, s, "shell", "rm", "/system/etc/permissions/scala_library.xml")
  274
+        implicit val emulator = false
  275
+        adbTask(d.absolutePath, emulator, s, "root")
  276
+        adbTask(d.absolutePath, emulator, s, "wait-for-device")
  277
+        adbTask(d.absolutePath, emulator, s, "remount")
  278
+        adbTask(d.absolutePath, emulator, s, "wait-for-device")
  279
+        adbTask(d.absolutePath, emulator, s, "shell", "rm", deviceJarPath(si.libraryJar, si.version))
  280
+        adbTask(d.absolutePath, emulator, s, "shell", "rm", devicePermissionPath(si.version))
  281
+        s.log.info("Scala has been removed from the " + deviceDesignation)
  282
+    }
  283
+
  284
+  private def unloadEmulatorTask(emulatorName: TaskKey[String]) =
  285
+    (emulatorName, toolsPath, sdkPath, dbPath, dxPath, target, scalaInstance, streams) map {
  286
+    (emulatorName, toolsPath, sdkPath, d, dxPath, target, si, s) =>
  287
+
  288
+        // We're using the emulator
  289
+        implicit val emulator = true
  290
+
  291
+        // Kill any running emulator
  292
+        doKillEmu (d, s)
  293
+
  294
+        // Restart the emulator in system read-write mode
  295
+        doStartEmuReadWrite (d, s, sdkPath, toolsPath, emulatorName, false)
  296
+
  297
+        // Remove the scala libs
  298
+        adbTask(d.absolutePath, emulator, s, "wait-for-device")
  299
+        adbTask(d.absolutePath, emulator, s, "root")
  300
+        adbTask(d.absolutePath, emulator, s, "wait-for-device")
  301
+        adbTask(d.absolutePath, emulator, s, "remount")
  302
+        adbTask(d.absolutePath, emulator, s, "wait-for-device")
  303
+        adbTask(d.absolutePath, emulator, s, "shell", "rm", deviceJarPath(si.libraryJar, si.version))
  304
+        adbTask(d.absolutePath, emulator, s, "shell", "rm", devicePermissionPath(si.version))
  305
+        doKillEmu (d, s)
  306
+
200 307
         s.log.info("Scala has been removed from the " + deviceDesignation)
201 308
     }
202 309
 
@@ -205,24 +312,14 @@ object AndroidPreload {
205 312
    *************************/
206 313
 
207 314
   lazy val settings: Seq[Setting[_]] = inConfig(Android) (Seq(
208  
-    // Device rooting/remounting
209  
-    rootDevice <<= commandTask("root")(false),
210  
-    rootEmulator <<= commandTask("root")(true),
211  
-    remountDevice <<= commandTask("remount")(false),
212  
-    remountDevice <<= remountDevice dependsOn (rootDevice),
213  
-    remountEmulator <<= commandTask("remount")(true),
214  
-    remountEmulator <<= remountEmulator dependsOn (rootEmulator),
215  
-
216  
-    // State checks
217  
-    preloadedDevice <<= preloadedTask(false) dependsOn (rootDevice),
218  
-    preloadedEmulator <<= preloadedTask(true) dependsOn (rootEmulator),
219  
-
220  
-    // Subtasks related to Scala preloading
221  
-    preloadDevice <<= preloadTask(false) dependsOn (remountDevice),
222  
-    preloadEmulator <<= preloadTask(true) dependsOn (remountEmulator),
  315
+    // Preload Scala on the device/emulator
  316
+    preloadDevice <<= preloadDeviceTask,
  317
+    preloadEmulator <<= InputTask(
  318
+      (sdkPath)(AndroidProject.installedAvds(_)))(preloadEmulatorTask),
223 319
 
224 320
     // Uninstall previously preloaded Scala
225  
-    unloadDevice <<= unloadTask(false) dependsOn(remountDevice),
226  
-    unloadEmulator <<= unloadTask(true) dependsOn(remountEmulator)
  321
+    unloadDevice <<= unloadDeviceTask,
  322
+    unloadEmulator <<= InputTask(
  323
+      (sdkPath)(AndroidProject.installedAvds(_)))(unloadEmulatorTask)
227 324
   ))
228 325
 }
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.