Merge "updater: Remove the redundant check on line count."
diff --git a/updater_sample/README.md b/updater_sample/README.md
index 8ec43d3..11b55eb 100644
--- a/updater_sample/README.md
+++ b/updater_sample/README.md
@@ -140,6 +140,9 @@
 no other update is running. The extra `key_value_pair_headers` will be
 included when fetching the payload.
 
+`key_value_pair_headers` argument also accepts properties other than HTTP Headers.
+List of allowed properties can be found in `system/update_engine/common/constants.cc`.
+
 ### UpdateEngine#cancel
 
 Cancel the ongoing update. The update could be running or suspended, but it
@@ -181,9 +184,8 @@
 - [x] Deferred switch slot demo
 - [x] Add UpdateManager; extract update logic from MainActivity
 - [x] Add Sample app update state (separate from update_engine status)
-- [-] Add smart update completion detection using onStatusUpdate
-- [ ] Add pause/resume demo
-- [ ] Add demo for passing NETWORK_ID to `UpdateEngine#applyPayload`
+- [x] Add smart update completion detection using onStatusUpdate
+- [x] Add pause/resume demo
 - [ ] Verify system partition checksum for package
 - [?] Add non-A/B updates demo
 
diff --git a/updater_sample/res/layout/activity_main.xml b/updater_sample/res/layout/activity_main.xml
index 7cde42c..b560827 100644
--- a/updater_sample/res/layout/activity_main.xml
+++ b/updater_sample/res/layout/activity_main.xml
@@ -197,6 +197,29 @@
                     android:text="Reset" />
             </LinearLayout>
 
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="12dp"
+                android:orientation="horizontal">
+
+                <Button
+                    android:id="@+id/buttonSuspend"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:onClick="onSuspendClick"
+                    android:text="Suspend" />
+
+                <Button
+                    android:id="@+id/buttonResume"
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:onClick="onResumeClick"
+                    android:text="Resume" />
+            </LinearLayout>
+
             <TextView
                 android:id="@+id/textViewUpdateInfo"
                 android:layout_width="wrap_content"
diff --git a/updater_sample/src/com/example/android/systemupdatersample/UpdateManager.java b/updater_sample/src/com/example/android/systemupdatersample/UpdateManager.java
index e4c0934..a9783e7 100644
--- a/updater_sample/src/com/example/android/systemupdatersample/UpdateManager.java
+++ b/updater_sample/src/com/example/android/systemupdatersample/UpdateManager.java
@@ -198,6 +198,24 @@
     }
 
     /**
+     * Suspend running update.
+     */
+    public synchronized void suspend() throws UpdaterState.InvalidTransitionException {
+        Log.d(TAG, "suspend invoked");
+        setUpdaterState(UpdaterState.PAUSED);
+        mUpdateEngine.cancel();
+    }
+
+    /**
+     * Resume suspended update.
+     */
+    public synchronized void resume() throws UpdaterState.InvalidTransitionException {
+        Log.d(TAG, "resume invoked");
+        setUpdaterState(UpdaterState.RUNNING);
+        updateEngineReApplyPayload();
+    }
+
+    /**
      * Updates {@link this.mState} and if state is changed,
      * it also notifies {@link this.mOnStateChangeCallback}.
      */
@@ -237,10 +255,6 @@
     /**
      * Requests update engine to stop any ongoing update. If an update has been applied,
      * leave it as is.
-     *
-     * <p>Sometimes it's possible that the
-     * update engine would throw an error when the method is called, and the only way to
-     * handle it is to catch the exception.</p>
      */
     public synchronized void cancelRunningUpdate() throws UpdaterState.InvalidTransitionException {
         Log.d(TAG, "cancelRunningUpdate invoked");
@@ -250,10 +264,6 @@
 
     /**
      * Resets update engine to IDLE state. If an update has been applied it reverts it.
-     *
-     * <p>Sometimes it's possible that the
-     * update engine would throw an error when the method is called, and the only way to
-     * handle it is to catch the exception.</p>
      */
     public synchronized void resetUpdate() throws UpdaterState.InvalidTransitionException {
         Log.d(TAG, "resetUpdate invoked");
@@ -506,7 +516,7 @@
             synchronizeUpdaterStateWithUpdateEngineStatus();
         }
 
-        getOnProgressUpdateCallback().ifPresent(callback -> callback.accept(progress));
+        getOnProgressUpdateCallback().ifPresent(callback -> callback.accept(mProgress.get()));
 
         if (previousStatus != status) {
             getOnEngineStatusUpdateCallback().ifPresent(callback -> callback.accept(status));
diff --git a/updater_sample/src/com/example/android/systemupdatersample/UpdaterState.java b/updater_sample/src/com/example/android/systemupdatersample/UpdaterState.java
index 573d336..4eb0b68 100644
--- a/updater_sample/src/com/example/android/systemupdatersample/UpdaterState.java
+++ b/updater_sample/src/com/example/android/systemupdatersample/UpdaterState.java
@@ -52,12 +52,12 @@
      */
     private static final ImmutableMap<Integer, ImmutableSet<Integer>> TRANSITIONS =
             ImmutableMap.<Integer, ImmutableSet<Integer>>builder()
-                    .put(IDLE, ImmutableSet.of(ERROR, RUNNING))
-                    .put(RUNNING, ImmutableSet.of(
-                            ERROR, PAUSED, REBOOT_REQUIRED, SLOT_SWITCH_REQUIRED))
-                    .put(PAUSED, ImmutableSet.of(ERROR, RUNNING, IDLE))
-                    .put(SLOT_SWITCH_REQUIRED, ImmutableSet.of(ERROR, IDLE))
+                    .put(IDLE, ImmutableSet.of(IDLE, ERROR, RUNNING))
                     .put(ERROR, ImmutableSet.of(IDLE))
+                    .put(RUNNING, ImmutableSet.of(
+                            IDLE, ERROR, PAUSED, REBOOT_REQUIRED, SLOT_SWITCH_REQUIRED))
+                    .put(PAUSED, ImmutableSet.of(ERROR, RUNNING, IDLE))
+                    .put(SLOT_SWITCH_REQUIRED, ImmutableSet.of(ERROR, REBOOT_REQUIRED, IDLE))
                     .put(REBOOT_REQUIRED, ImmutableSet.of(IDLE))
                     .build();
 
diff --git a/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java b/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java
index 6c71cb6..fc9fddd 100644
--- a/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java
+++ b/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java
@@ -55,6 +55,8 @@
     private Button mButtonApplyConfig;
     private Button mButtonStop;
     private Button mButtonReset;
+    private Button mButtonSuspend;
+    private Button mButtonResume;
     private ProgressBar mProgressBar;
     private TextView mTextViewUpdaterState;
     private TextView mTextViewEngineStatus;
@@ -79,6 +81,8 @@
         this.mButtonApplyConfig = findViewById(R.id.buttonApplyConfig);
         this.mButtonStop = findViewById(R.id.buttonStop);
         this.mButtonReset = findViewById(R.id.buttonReset);
+        this.mButtonSuspend = findViewById(R.id.buttonSuspend);
+        this.mButtonResume = findViewById(R.id.buttonResume);
         this.mProgressBar = findViewById(R.id.progressBar);
         this.mTextViewUpdaterState = findViewById(R.id.textViewUpdaterState);
         this.mTextViewEngineStatus = findViewById(R.id.textViewEngineStatus);
@@ -209,9 +213,34 @@
     }
 
     /**
+     * suspend button clicked
+     */
+    public void onSuspendClick(View view) {
+        try {
+            mUpdateManager.suspend();
+        } catch (UpdaterState.InvalidTransitionException e) {
+            Log.e(TAG, "Failed to suspend running update", e);
+        }
+    }
+
+    /**
+     * resume button clicked
+     */
+    public void onResumeClick(View view) {
+        try {
+            uiResetWidgets();
+            uiResetEngineText();
+            mUpdateManager.resume();
+        } catch (UpdaterState.InvalidTransitionException e) {
+            Log.e(TAG, "Failed to resume running update", e);
+        }
+    }
+
+    /**
      * switch slot button clicked
      */
     public void onSwitchSlotClick(View view) {
+        uiResetWidgets();
         mUpdateManager.setSwitchSlotOnReboot();
     }
 
@@ -289,6 +318,8 @@
         mButtonApplyConfig.setEnabled(false);
         mButtonStop.setEnabled(false);
         mButtonReset.setEnabled(false);
+        mButtonSuspend.setEnabled(false);
+        mButtonResume.setEnabled(false);
         mProgressBar.setEnabled(false);
         mProgressBar.setVisibility(ProgressBar.INVISIBLE);
         mButtonSwitchSlot.setEnabled(false);
@@ -303,6 +334,7 @@
 
     private void uiStateIdle() {
         uiResetWidgets();
+        mButtonReset.setEnabled(true);
         mSpinnerConfigs.setEnabled(true);
         mButtonReload.setEnabled(true);
         mButtonApplyConfig.setEnabled(true);
@@ -314,6 +346,7 @@
         mProgressBar.setEnabled(true);
         mProgressBar.setVisibility(ProgressBar.VISIBLE);
         mButtonStop.setEnabled(true);
+        mButtonSuspend.setEnabled(true);
     }
 
     private void uiStatePaused() {
@@ -321,6 +354,7 @@
         mButtonReset.setEnabled(true);
         mProgressBar.setEnabled(true);
         mProgressBar.setVisibility(ProgressBar.VISIBLE);
+        mButtonResume.setEnabled(true);
     }
 
     private void uiStateSlotSwitchRequired() {
diff --git a/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java b/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java
index 3ba84c1..0308693 100644
--- a/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java
+++ b/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java
@@ -65,7 +65,7 @@
         mTargetContext = InstrumentationRegistry.getTargetContext();
         mTestContext = InstrumentationRegistry.getContext();
 
-        mTestDir = mTargetContext.getFilesDir();
+        mTestDir = mTargetContext.getCacheDir();
         mPayloadSpecs = new PayloadSpecs();
     }
 
diff --git a/updater_sample/tools/gen_update_config.py b/updater_sample/tools/gen_update_config.py
index 7fb64f7..b43e49d 100755
--- a/updater_sample/tools/gen_update_config.py
+++ b/updater_sample/tools/gen_update_config.py
@@ -131,10 +131,10 @@
                         choices=ab_install_type_choices,
                         help='A/B update installation type')
     parser.add_argument('--ab_force_switch_slot',
-                        type=bool,
                         default=False,
-                        help='if set true device will boot to a new slot, otherwise user manually '
-                             'switches slot on the screen')
+                        action='store_true',
+                        help='if set device will boot to a new slot, otherwise user '
+                              'manually switches slot on the screen')
     parser.add_argument('package',
                         type=str,
                         help='OTA package zip file')