3 # Copyright (C) 2008 The Android Open Source Project
5 # Licensed under the Apache License, Version 2.0 (the 'License');
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an 'AS IS' BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
18 """Creates optimized versions of APK files.
20 A tool and associated functions to communicate with an Android
21 emulator instance, run commands, and scrape out files.
23 Requires at least python2.4.
41 _emulator_popen = None
45 def EnsureTempDir(path=None):
46 """Creates a temporary directory and returns its path.
48 Creates any necessary parent directories.
51 path: If specified, used as the temporary directory. If not specified,
52 a safe temporary path is created. The caller is responsible for
53 deleting the directory.
56 The path to the new directory, or None if a problem occurred.
59 path = tempfile.mkdtemp('', 'dexpreopt-')
60 elif not os.path.exists(path):
62 elif not os.path.isdir(path):
67 def CreateZeroedFile(path, length):
68 """Creates the named file and writes <length> zero bytes to it.
70 Unlinks the file first if it already exists.
71 Creates its containing directory if necessary.
74 path: The path to the file to create.
75 length: The number of zero bytes to write to the file.
80 subprocess.call(['rm', '-f', path])
81 d = os.path.dirname(path)
82 if d and not os.path.exists(d): os.makedirs(os.path.dirname(d))
83 # TODO: redirect child's stdout to /dev/null
84 ret = subprocess.call(['dd', 'if=/dev/zero', 'of=%s' % path,
85 'bs=%d' % length, 'count=1'])
86 return not ret # i.e., ret == 0; i.e., the child exited successfully.
89 def StartEmulator(exe_name='emulator', kernel=None,
90 ramdisk=None, image=None, userdata=None, system=None):
91 """Runs the emulator with the specified arguments.
94 exe_name: The name of the emulator to run. May be absolute, relative,
95 or unqualified (and left to exec() to find).
96 kernel: If set, passed to the emulator as "-kernel".
97 ramdisk: If set, passed to the emulator as "-ramdisk".
98 image: If set, passed to the emulator as "-system".
99 userdata: If set, passed to the emulator as "-initdata" and "-data".
100 system: If set, passed to the emulator as "-sysdir".
103 A subprocess.Popen that refers to the emulator process, or None if
106 #exe_name = './stuff'
108 if kernel: args += ['-kernel', kernel]
109 if ramdisk: args += ['-ramdisk', ramdisk]
110 if image: args += ['-system', image]
111 if userdata: args += ['-initdata', userdata, '-data', userdata]
112 if system: args += ['-sysdir', system]
113 args += ['-partition-size', '128']
114 args += ['-no-window', '-netfast', '-noaudio']
119 # Use dedicated fds instead of stdin/out to talk to the
120 # emulator so that the emulator doesn't try to tty-cook
122 em_stdin_r, em_stdin_w = posix.pipe()
123 em_stdout_r, em_stdout_w = posix.pipe()
124 args += ['-shell-serial', 'fdpair:%d:%d' % (em_stdin_r, em_stdout_w)]
128 # Ensure that this environment variable isn't set;
129 # if it is, the emulator will print the log to stdout.
130 if os.environ.get('ANDROID_LOG_TAGS'):
131 del os.environ['ANDROID_LOG_TAGS']
134 # bufsize=1 line-buffered, =0 unbuffered,
135 # <0 system default (fully buffered)
136 Trace('Running emulator: %s' % ' '.join(args))
138 ep = subprocess.Popen(args)
140 ep = subprocess.Popen(args, close_fds=True,
141 stdin=subprocess.PIPE,
142 stdout=subprocess.PIPE,
143 stderr=subprocess.PIPE)
146 # Hijack the Popen.stdin/.stdout fields to point to our
147 # pipes. These are the same fields that would have been set
148 # if we called Popen() with stdin=subprocess.PIPE, etc.
149 # Note that these names are from the point of view of the
152 # Since we'll be using select.select() to read data a byte
153 # at a time, it's important that these files are unbuffered
154 # (bufsize=0). If Popen() took care of the pipes, they're
155 # already unbuffered.
156 ep.stdin = os.fdopen(em_stdin_w, 'w', 0)
157 ep.stdout = os.fdopen(em_stdout_r, 'r', 0)
160 print >>sys.stderr, 'Could not start emulator:', e
164 def IsDataAvailable(fo, timeout=0):
165 """Indicates whether or not data is available to be read from a file object.
168 fo: A file object to read from.
169 timeout: The number of seconds to wait for data, or zero for no timeout.
172 True iff data is available to be read.
174 return select.select([fo], [], [], timeout) == ([fo], [], [])
177 def ConsumeAvailableData(fo):
178 """Reads data from a file object while it's available.
180 Stops when no more data is immediately available or upon reaching EOF.
183 fo: A file object to read from.
186 An unsigned byte array.array of the data that was read.
188 buf = array.array('B')
189 while IsDataAvailable(fo):
197 def ShowTimeout(timeout, end_time):
198 """For debugging, display the timeout info.
201 timeout: the timeout in seconds.
202 end_time: a time.time()-based value indicating when the timeout should
207 remaining = end_time - time.time()
208 Trace('ok, time remaining %.1f of %.1f' % (remaining, timeout))
210 Trace('ok (no timeout)')
213 def WaitForString(inf, pattern, timeout=0, max_len=0, eat_to_eol=True,
214 reset_on_activity=False):
215 """Reads from a file object and returns when the pattern matches the data.
217 Reads a byte at a time to avoid consuming extra data, so do not call
218 this function when you expect the pattern to match a large amount of data.
221 inf: The file object to read from.
222 pattern: The string to look for in the input data.
223 May be a tuple of strings.
224 timeout: How long to wait, in seconds. No timeout if it evaluates to False.
225 max_len: Return None if this many bytes have been read without matching.
226 No upper bound if it evaluates to False.
227 eat_to_eol: If true, the input data will be consumed until a '\\n' or EOF
229 reset_on_activity: If True, reset the timeout whenever a character is
233 The input data matching the expression as an unsigned char array,
234 or None if the operation timed out or didn't match after max_len bytes.
237 IOError: An error occurred reading from the input file.
240 end_time = time.time() + timeout
245 Trace('WaitForString: "%s", %.1f' % (pattern, timeout))
247 buf = array.array('B') # unsigned char array
251 remaining = end_time - time.time()
253 Trace('Timeout expired after %.1f seconds' % timeout)
258 if IsDataAvailable(inf, remaining):
259 if reset_on_activity and timeout:
260 end_time = time.time() + timeout
264 c = buf.tostring()[-1:]
266 if ci < 0x20: c = '.'
268 print 'read [%c] 0x%02x' % (c, ci)
271 if buf.tostring().endswith(pattern):
274 Trace('Matched; eating to EOL')
277 ShowTimeout(timeout, end_time)
280 print '/%s/ ? "%s"' % (pattern, buf.tostring())
282 if buf.tostring()[-1:] == '\n':
283 ShowTimeout(timeout, end_time)
286 if max_len and len(buf) >= max_len: return None
289 def WaitForEmulator(ep, timeout=0):
290 """Waits for the emulator to start up and print the first prompt.
293 ep: A subprocess.Popen object referring to the emulator process.
294 timeout: How long to wait, in seconds. No timeout if it evaluates to False.
297 True on success, False if the timeout occurred.
299 # Prime the pipe; the emulator doesn't start without this.
302 # Wait until the console is ready and the first prompt appears.
303 buf = WaitForString(ep.stdout, '#', timeout=timeout, eat_to_eol=False)
305 Trace('Saw the prompt: "%s"' % buf.tostring())
310 def WaitForPrompt(ep, prompt=None, timeout=0, reset_on_activity=False):
311 """Blocks until the prompt appears on ep.stdout or the timeout elapses.
314 ep: A subprocess.Popen connection to the emulator process.
315 prompt: The prompt to wait for. If None, uses ep.prompt.
316 timeout: How many seconds to wait for the prompt. Waits forever
318 reset_on_activity: If True, reset the timeout whenever a character is
322 A string containing the data leading up to the prompt. The string
323 will always end in '\\n'. Returns None if the prompt was not seen
324 within the timeout, or if some other error occurred.
326 if not prompt: prompt = ep.prompt
328 #Trace('waiting for prompt "%s"' % prompt)
329 data = WaitForString(ep.stdout, prompt,
330 timeout=timeout, reset_on_activity=reset_on_activity)
332 # data contains everything on ep.stdout up to and including the prompt,
333 # plus everything up 'til the newline. Scrape out the prompt
334 # and everything that follows, and ensure that the result ends
335 # in a newline (which is important if it would otherwise be empty).
342 print 'WaitForPrompt saw """\n%s"""' % s
347 def ReplaceEmulatorPrompt(ep, prompt=None):
348 """Replaces PS1 in the emulator with a different value.
350 This is useful for making the prompt unambiguous; i.e., something
351 that probably won't appear in the output of another command.
353 Assumes that the emulator is already sitting at a prompt,
354 waiting for shell input.
356 Puts the new prompt in ep.prompt.
359 ep: A subprocess.Popen object referring to the emulator process.
360 prompt: The new prompt to use
363 True on success, False if the timeout occurred.
366 prompt = '-----DEXPREOPT-PROMPT-----'
367 print >>ep.stdin, 'PS1="%s\n"' % prompt
370 # Eat the command echo.
371 data = WaitForPrompt(ep, timeout=2)
375 # Make sure it's actually there.
376 return WaitForPrompt(ep, timeout=2)
379 def RunEmulatorCommand(ep, cmd, timeout=0):
380 """Sends the command to the emulator's shell and waits for the result.
382 Assumes that the emulator is already sitting at a prompt,
383 waiting for shell input.
386 ep: A subprocess.Popen object referring to the emulator process.
387 cmd: The shell command to run in the emulator.
388 timeout: The number of seconds to wait for the command to complete,
389 or zero for no timeout.
392 If the command ran and returned to the console prompt before the
393 timeout, returns the output of the command as a string.
394 Returns None otherwise.
396 ConsumeAvailableData(ep.stdout)
398 Trace('Running "%s"' % cmd)
399 print >>ep.stdin, '%s' % cmd
401 # The console will echo the command.
402 #Trace('Waiting for echo')
403 if WaitForString(ep.stdout, cmd, timeout=timeout):
404 #Trace('Waiting for completion')
405 return WaitForPrompt(ep, timeout=timeout, reset_on_activity=True)
410 def ReadFileList(ep, dir_list, timeout=0):
411 """Returns a list of emulator files in each dir in dir_list.
414 ep: A subprocess.Popen object referring to the emulator process.
415 dir_list: List absolute paths to directories to read.
416 timeout: The number of seconds to wait for the command to complete,
417 or zero for no timeout.
420 A list of absolute paths to files in the named directories,
421 in the context of the emulator's filesystem.
426 output = RunEmulatorCommand(ep, 'ls ' + d, timeout=timeout)
428 Trace('Could not ls ' + d)
430 ret += ['%s/%s' % (d, f) for f in output.splitlines()]
434 def DownloadDirectoryHierarchy(ep, src, dest, timeout=0):
435 """Recursively downloads an emulator directory to the local filesystem.
438 ep: A subprocess.Popen object referring to the emulator process.
439 src: The path on the emulator's filesystem to download from.
440 dest: The path on the local filesystem to download to.
441 timeout: The number of seconds to wait for the command to complete,
442 or zero for no timeout. (CURRENTLY IGNORED)
445 True iff the files downloaded successfully, False otherwise.
447 ConsumeAvailableData(ep.stdout)
449 if not os.path.exists(dest):
452 cmd = 'afar %s' % src
453 Trace('Running "%s"' % cmd)
454 print >>ep.stdin, '%s' % cmd
456 # The console will echo the command.
457 #Trace('Waiting for echo')
458 if not WaitForString(ep.stdout, cmd, timeout=timeout):
461 #TODO: use a signal to support timing out?
464 # Android File Archive format:
466 # magic[5]: 'A' 'F' 'A' 'R' '\n'
467 # version[4]: 0x00 0x00 0x00 0x01
469 # file magic[4]: 'F' 'I' 'L' 'E'
470 # namelen[4]: Length of file name, including NUL byte (big-endian)
471 # name[*]: NUL-terminated file name
472 # datalen[4]: Length of file (big-endian)
473 # data[*]: Unencoded file data
474 # adler32[4]: adler32 of the unencoded file data (big-endian)
475 # file end magic[4]: 'f' 'i' 'l' 'e'
476 # end magic[4]: 'E' 'N' 'D' 0x00
480 HEADER = array.array('B', 'AFAR\n\000\000\000\001')
481 buf = array.array('B')
482 buf.fromfile(ep.stdout, len(HEADER))
484 Trace('Header does not match: "%s"' % buf)
487 # Read the file entries.
488 FILE_START = array.array('B', 'FILE')
489 FILE_END = array.array('B', 'file')
490 END = array.array('B', 'END\000')
493 buf = array.array('B')
494 buf.fromfile(ep.stdout, 4)
495 if buf == FILE_START:
496 # Name length (4 bytes, big endian)
497 buf = array.array('B')
498 buf.fromfile(ep.stdout, 4)
499 (name_len,) = struct.unpack('>I', buf)
500 #Trace('name len %d' % name_len)
502 # Name, NUL-terminated.
503 buf = array.array('B')
504 buf.fromfile(ep.stdout, name_len)
505 buf.pop() # Remove trailing NUL byte.
506 file_name = buf.tostring()
507 Trace('FILE: %s' % file_name)
509 # File length (4 bytes, big endian)
510 buf = array.array('B')
511 buf.fromfile(ep.stdout, 4)
512 (file_len,) = struct.unpack('>I', buf)
515 data = array.array('B')
516 data.fromfile(ep.stdout, file_len)
517 #Trace('FILE: read %d bytes from %s' % (file_len, file_name))
519 # adler32 (4 bytes, big endian)
520 buf = array.array('B')
521 buf.fromfile(ep.stdout, 4)
522 (adler32,) = struct.unpack('>i', buf) # adler32 wants a signed int ('i')
523 data_adler32 = zlib.adler32(data)
524 # Because of a difference in behavior of zlib.adler32 on 32-bit and 64-bit
525 # systems (one returns signed values, the other unsigned), we take the
526 # modulo 2**32 of the checksums, and compare those.
527 # See also http://bugs.python.org/issue1202
528 if (adler32 % (2**32)) != (data_adler32 % (2**32)):
529 Trace('adler32 does not match: calculated 0x%08x != expected 0x%08x' %
530 (data_adler32, adler32))
534 buf = array.array('B')
535 buf.fromfile(ep.stdout, 4)
537 Trace('Unexpected file end magic "%s"' % buf)
540 # Write to the output file
541 out_file_name = dest + '/' + file_name[len(src):]
542 p = os.path.dirname(out_file_name)
543 if not os.path.exists(p): os.makedirs(p)
544 fo = file(out_file_name, 'w+b')
546 Trace('FILE: Writing %d bytes to %s' % (len(data), out_file_name))
553 Trace('Unexpected magic "%s"' % buf)
556 return WaitForPrompt(ep, timeout=timeout, reset_on_activity=True)
559 def ReadBootClassPath(ep, timeout=0):
560 """Reads and returns the default bootclasspath as a list of files.
563 ep: A subprocess.Popen object referring to the emulator process.
564 timeout: The number of seconds to wait for the command to complete,
565 or zero for no timeout.
568 The bootclasspath as a list of strings.
571 bcp = RunEmulatorCommand(ep, 'echo $BOOTCLASSPATH', timeout=timeout)
573 Trace('Could not find bootclasspath')
575 return bcp.strip().split(':') # strip trailing newline
578 def RunDexoptOnFileList(ep, files, dest_root, move=False, timeout=0):
579 """Creates the corresponding .odex file for all jar/apk files in 'files'.
580 Copies the .odex file to a location under 'dest_root'. If 'move' is True,
581 the file is moved instead of copied.
584 ep: A subprocess.Popen object referring to the emulator process.
585 files: The list of files to optimize
586 dest_root: directory to copy/move odex files to. Must already exist.
587 move: if True, move rather than copy files
588 timeout: The number of seconds to wait for the command to complete,
589 or zero for no timeout.
592 True on success, False on failure.
594 for jar_file in files:
595 if jar_file.endswith('.apk') or jar_file.endswith('.jar'):
596 odex_file = jar_file[:jar_file.rfind('.')] + '.odex'
597 cmd = 'dexopt-wrapper %s %s' % (jar_file, odex_file)
598 if not RunEmulatorCommand(ep, cmd, timeout=timeout):
599 Trace('"%s" failed' % cmd)
602 # Always copy the odex file. There's no cp(1), so we
603 # cat out to the new file.
604 dst_odex = dest_root + odex_file
605 cmd = 'cat %s > %s' % (odex_file, dst_odex) # no cp(1)
606 if not RunEmulatorCommand(ep, cmd, timeout=timeout):
607 Trace('"%s" failed' % cmd)
610 # Move it if we're asked to. We can't use mv(1) because
611 # the files tend to move between filesystems.
613 cmd = 'rm %s' % odex_file
614 if not RunEmulatorCommand(ep, cmd, timeout=timeout):
615 Trace('"%s" failed' % cmd)
620 def InstallCacheFiles(cache_system_dir, out_system_dir):
621 """Install files in cache_system_dir to the proper places in out_system_dir.
623 cache_system_dir contains various files from /system, plus .odex files
624 for most of the .apk/.jar files that live there.
625 This function copies each .odex file from the cache dir to the output dir
626 and removes "classes.dex" from each appropriate .jar/.apk.
628 E.g., <cache_system_dir>/app/NotePad.odex would be copied to
629 <out_system_dir>/app/NotePad.odex, and <out_system_dir>/app/NotePad.apk
630 would have its classes.dex file removed.
633 cache_system_dir: The directory containing the cache files scraped from
635 out_system_dir: The local directory that corresponds to "/system"
636 on the device filesystem. (the root of system.img)
639 True if everything succeeded, False if any problems occurred.
641 # First, walk through cache_system_dir and copy every .odex file
642 # over to out_system_dir, ensuring that the destination directory
643 # contains the corresponding source file.
644 for root, dirs, files in os.walk(cache_system_dir):
646 if name.endswith('.odex'):
647 odex_file = os.path.join(root, name)
649 # Find the path to the .odex file's source apk/jar file.
650 out_stem = odex_file[len(cache_system_dir):odex_file.rfind('.')]
651 out_stem = out_system_dir + out_stem;
652 jar_file = out_stem + '.jar'
653 if not os.path.exists(jar_file):
654 jar_file = out_stem + '.apk'
655 if not os.path.exists(jar_file):
656 Trace('Cannot find source .jar/.apk for %s: %s' %
657 (odex_file, out_stem + '.{jar,apk}'))
660 # Copy the cache file next to the source file.
661 cmd = ['cp', odex_file, out_stem + '.odex']
662 ret = subprocess.call(cmd)
663 if ret: # non-zero exit status
664 Trace('%s failed' % ' '.join(cmd))
667 # Walk through the output /system directory, making sure
668 # that every .jar/.apk has an odex file. While we do this,
669 # remove the classes.dex entry from each source archive.
670 for root, dirs, files in os.walk(out_system_dir):
672 if name.endswith('.apk') or name.endswith('.jar'):
673 jar_file = os.path.join(root, name)
674 odex_file = jar_file[:jar_file.rfind('.')] + '.odex'
675 if not os.path.exists(odex_file):
676 if root.endswith('/system/app') or root.endswith('/system/framework'):
677 Trace('jar/apk %s has no .odex file %s' % (jar_file, odex_file))
682 # Attempting to dexopt a jar with no classes.dex currently
683 # creates a 40-byte odex file.
684 # TODO: use a more reliable check
685 if os.path.getsize(odex_file) > 100:
686 # Remove classes.dex from the .jar file.
687 cmd = ['zip', '-dq', jar_file, 'classes.dex']
688 ret = subprocess.call(cmd)
689 if ret: # non-zero exit status
690 Trace('"%s" failed' % ' '.join(cmd))
693 # Some of the apk files don't contain any code.
694 if not name.endswith('.apk'):
695 Trace('%s has a zero-length odex file' % jar_file)
697 cmd = ['rm', odex_file]
698 ret = subprocess.call(cmd)
699 if ret: # non-zero exit status
700 Trace('"%s" failed' % ' '.join(cmd))
706 def KillChildProcess(p, sig=signal.SIGTERM, timeout=0):
707 """Waits for a child process to die without getting stuck in wait().
709 After Jean Brouwers's 2004 post to python-list.
712 p: A subprocess.Popen representing the child process to kill.
713 sig: The signal to send to the child process.
714 timeout: How many seconds to wait for the child process to die.
715 If zero, do not time out.
718 The exit status of the child process, if it was successfully killed.
719 The final value of p.returncode if it wasn't.
728 os.kill(p.pid, signal.SIGKILL)
738 """Prints a message to stdout.
741 msg: The message to print.
743 #print 'dexpreopt: %s' % msg
744 when = datetime.datetime.now()
745 print '%02d:%02d.%d dexpreopt: %s' % (when.minute, when.second, when.microsecond, msg)
749 """Attempts to kill the emulator process, if it is running.
752 The exit status of the emulator process, or None if the emulator
753 was not running or was unable to be killed.
755 global _emulator_popen
757 Trace('Killing emulator')
759 ret = KillChildProcess(_emulator_popen, sig=signal.SIGINT, timeout=5)
761 Trace('Could not kill emulator')
763 _emulator_popen = None
769 """Prints an error and causes the process to exit.
772 msg: Additional error string to print (optional).
777 s = 'dexpreopt: ERROR'
778 if msg: s += ': %s' % msg
779 print >>sys.stderr, msg
784 def PrintUsage(msg=None):
785 """Prints commandline usage information for the tool and exits with an error.
788 msg: Additional string to print (optional).
794 print >>sys.stderr, 'dexpreopt: %s', msg
795 print >>sys.stderr, """Usage: dexpreopt <options>
797 -kernel <kernel file> Kernel to use when running the emulator
798 -ramdisk <ramdisk.img file> Ramdisk to use when running the emulator
799 -image <system.img file> System image to use when running the
800 emulator. /system/app should contain the
801 .apk files to optimize, and any required
802 bootclasspath libraries must be present
803 in the correct locations.
804 -system <path> The product directory, which usually contains
805 files like 'system.img' (files other than
806 the kernel in that directory won't
808 -outsystemdir <path> A fully-populated /system directory, ready
809 to be modified to contain the optimized
810 files. The appropriate .jar/.apk files
811 will be stripped of their classes.dex
812 entries, and the optimized .dex files
813 will be added alongside the packages
816 -tmpdir <path> If specified, use this directory for
817 intermediate objects. If not specified,
818 a unique directory under the system
825 """Parses commandline arguments.
828 argv: A list of arguments; typically sys.argv[1:]
831 A tuple containing two dictionaries; the first contains arguments
832 that will be passsed to the emulator, and the second contains other
835 parser = optparse.OptionParser()
837 parser.add_option('--kernel', help='Passed to emulator')
838 parser.add_option('--ramdisk', help='Passed to emulator')
839 parser.add_option('--image', help='Passed to emulator')
840 parser.add_option('--system', help='Passed to emulator')
841 parser.add_option('--outsystemdir', help='Destination /system directory')
842 parser.add_option('--tmpdir', help='Optional temp directory to use')
844 options, args = parser.parse_args(args=argv)
845 if args: PrintUsage()
849 if options.kernel: emulator_args['kernel'] = options.kernel
850 if options.ramdisk: emulator_args['ramdisk'] = options.ramdisk
851 if options.image: emulator_args['image'] = options.image
852 if options.system: emulator_args['system'] = options.system
853 if options.outsystemdir: other_args['outsystemdir'] = options.outsystemdir
854 if options.tmpdir: other_args['tmpdir'] = options.tmpdir
856 return (emulator_args, other_args)
859 def DexoptEverything(ep, dest_root):
860 """Logic for finding and dexopting files in the necessary order.
863 ep: A subprocess.Popen object referring to the emulator process.
864 dest_root: directory to copy/move odex files to
867 True on success, False on failure.
871 if not RunEmulatorCommand(ep, 'ls /system/app', timeout=5):
874 # We're very short on space, so remove a bunch of big stuff that we
876 cmd = 'rm -r /system/sounds /system/media /system/fonts /system/xbin'
877 if not RunEmulatorCommand(ep, cmd, timeout=40):
878 Trace('"%s" failed' % cmd)
881 Trace('Read file list')
882 jar_dirs = ['/system/framework', '/system/app']
883 files = ReadFileList(ep, jar_dirs, timeout=5)
885 Fail('Could not list files in %s' % ' '.join(jar_dirs))
886 #Trace('File list:\n"""\n%s\n"""' % '\n'.join(files))
888 bcp = ReadBootClassPath(ep, timeout=2)
890 Fail('Could not sort by bootclasspath')
892 # Remove bootclasspath entries from the main file list.
897 Trace('File list does not contain bootclasspath entry "%s"' % jar)
900 # Create the destination directories.
901 for d in ['', '/system'] + jar_dirs:
902 cmd = 'mkdir %s%s' % (dest_root, d)
903 if not RunEmulatorCommand(ep, cmd, timeout=4):
904 Trace('"%s" failed' % cmd)
907 # First, dexopt the bootclasspath. Keep their cache files in place.
908 Trace('Dexopt %d bootclasspath files' % len(bcp))
909 if not RunDexoptOnFileList(ep, bcp, dest_root, timeout=120):
910 Trace('Could not dexopt bootclasspath')
913 # dexopt the rest. To avoid running out of space on the emulator
914 # volume, move each cache file after it's been created.
915 Trace('Dexopt %d files' % len(files))
916 if not RunDexoptOnFileList(ep, files, dest_root, move=True, timeout=120):
917 Trace('Could not dexopt files')
921 if not RunEmulatorCommand(ep, 'ls /system/app', timeout=5):
929 """Main function that can be wrapped in a try block.
934 emulator_args, other_args = ParseArgs(sys.argv[1:])
936 tmp_dir = EnsureTempDir(other_args.get('tmpdir'))
937 if not tmp_dir: Fail('Could not create temp dir')
939 Trace('Creating data image')
940 userdata = '%s/data.img' % tmp_dir
941 if not CreateZeroedFile(userdata, 32 * 1024 * 1024):
942 Fail('Could not create data image file')
943 emulator_args['userdata'] = userdata
945 ep = StartEmulator(**emulator_args)
946 if not ep: Fail('Could not start emulator')
947 global _emulator_popen
950 # TODO: unlink the big userdata file now, since the emulator
953 if not WaitForEmulator(ep, timeout=20): Fail('Emulator did not respond')
954 if not ReplaceEmulatorPrompt(ep): Fail('Could not replace prompt')
956 dest_root = '/data/dexpreopt-root'
957 if not DexoptEverything(ep, dest_root): Fail('Could not dexopt files')
959 # Grab the odex files that were left in dest_root.
960 cache_system_dir = tmp_dir + '/cache-system'
961 if not DownloadDirectoryHierarchy(ep, dest_root + '/system',
964 Fail('Could not download %s/system from emulator' % dest_root)
966 if not InstallCacheFiles(cache_system_dir=cache_system_dir,
967 out_system_dir=other_args['outsystemdir']):
968 Fail('Could not install files')
970 Trace('dexpreopt successful')
981 if __name__ == '__main__':