Add "warm" starting to mobile-install.
This introduces a new way to stop applications when deploying incremental
changes that saves the current app state for the next run. This allows things
like the back stack, and View/Fragment/Activity saved state to be restored when
the app next launches, making it easier to quickly iterate on code changes.
It adds a "--start" flag to mobile-install that replaces "--start_app".
--start accepts an argument describing the mode: no, cold, or warm. "no" is
now the equivalent of "--nostart_app", "cold" is the equivalent of
"--start_app", and "warm" is the new start mode.
Note that this is only useful with incremental installs, as Android clears out
any previously saved state when an APK is replaced.
--
MOS_MIGRATED_REVID=99508790
diff --git a/tools/android/incremental_install.py b/tools/android/incremental_install.py
index 4c7faa7..09fa849 100644
--- a/tools/android/incremental_install.py
+++ b/tools/android/incremental_install.py
@@ -46,8 +46,11 @@
"The number of instances of adb to use in parallel to "
"update files on the device",
lower_bound=1)
-gflags.DEFINE_boolean("start_app", False, "Whether to start the app after "
- "installing it.")
+gflags.DEFINE_enum("start", "no", ["no", "cold", "warm"], "Whether/how to "
+ "start the app after installing it. 'cold' and 'warm' will "
+ "both cause the app to be started, 'warm' will start it "
+ "with previously saved application state.")
+gflags.DEFINE_boolean("start_app", False, "Deprecated, use 'start'.")
gflags.DEFINE_string("user_home_dir", None, "Path to the user's home directory")
gflags.DEFINE_string("flagfile", None,
"Path to a file to read additional flags from")
@@ -247,6 +250,14 @@
"""Force stops the app with the given package."""
self._Shell("am force-stop %s" % package)
+ def StopAppAndSaveState(self, package):
+ """Stops the app with the given package, saving state for the next run."""
+ # 'am kill' will only kill processes in the background, so we must make sure
+ # our process is in the background first. We accomplish this by bringing up
+ # the app switcher.
+ self._Shell("input keyevent KEYCODE_APP_SWITCH")
+ self._Shell("am kill %s" % package)
+
def StartApp(self, package):
"""Starts the app with the given package."""
self._Shell("monkey -p %s -c android.intent.category.LAUNCHER 1" % package)
@@ -553,7 +564,7 @@
def IncrementalInstall(adb_path, execroot, stub_datafile, output_marker,
- adb_jobs, start_app, dexmanifest=None, apk=None,
+ adb_jobs, start_type, dexmanifest=None, apk=None,
native_libs=None, resource_apk=None,
split_main_apk=None, split_apks=None,
user_home_dir=None):
@@ -565,7 +576,8 @@
stub_datafile: The stub datafile containing the app's package name.
output_marker: Path to the output marker file.
adb_jobs: The number of instances of adb to use in parallel.
- start_app: If True, starts the app after updating.
+ start_type: A string describing whether/how to start the app after
+ installing it. Can be 'no', 'cold', or 'warm'.
dexmanifest: Path to the .dex manifest file.
apk: Path to the .apk file. May be None to perform an incremental install.
native_libs: Native libraries to install.
@@ -606,9 +618,12 @@
future.result()
else:
- adb.StopApp(app_package)
+ if start_type == "warm":
+ adb.StopAppAndSaveState(app_package)
+ else:
+ adb.StopApp(app_package)
- if start_app:
+ if start_type in ["cold", "warm"]:
logging.info("Starting application %s", app_package)
adb.StartApp(app_package)
@@ -640,13 +655,17 @@
fmt = "%(message)s"
logging.basicConfig(stream=sys.stdout, level=level, format=fmt)
+ start_type = FLAGS.start
+ if FLAGS.start_app and start_type == "no":
+ start_type = "cold"
+
IncrementalInstall(
adb_path=FLAGS.adb,
adb_jobs=FLAGS.adb_jobs,
execroot=FLAGS.execroot,
stub_datafile=FLAGS.stub_datafile,
output_marker=FLAGS.output_marker,
- start_app=FLAGS.start_app,
+ start_type=start_type,
native_libs=FLAGS.native_lib,
split_main_apk=FLAGS.split_main_apk,
split_apks=FLAGS.split_apk,