Merge "updater_sample: add http header demo"
diff --git a/updater_sample/README.md b/updater_sample/README.md
index 2c1f0ce..7e25b07 100644
--- a/updater_sample/README.md
+++ b/updater_sample/README.md
@@ -61,6 +61,17 @@
 6. Push OTA packages to the device.
 
 
+## Sending HTTP headers from UpdateEngine
+
+Sometimes OTA package server might require some HTTP headers to be present,
+e.g. `Authorization` header to contain valid auth token. While performing
+streaming update, `UpdateEngine` allows passing on certain HTTP headers;
+as of writing this sample app, these headers are `Authorization` and `User-Agent`.
+
+`android.os.UpdateEngine#applyPayload` contains information on
+which HTTP headers are supported.
+
+
 ## Development
 
 - [x] Create a UI with list of configs, current version,
@@ -72,12 +83,12 @@
 - [x] Prepare streaming update (partially downloading package)
 - [x] Add applying streaming update
 - [x] Add stop/reset the update
+- [x] Add demo for passing HTTP headers to `UpdateEngine#applyPayload`
 - [ ] Add tests for `MainActivity`
-- [ ] Verify system partition checksum for package
 - [ ] HAL compatibility check
 - [ ] Change partition demo
+- [ ] Verify system partition checksum for package
 - [ ] Add non-A/B updates demo
-- [ ] Add docs for passing HTTP headers to `UpdateEngine#applyPayload`
 
 
 ## Running tests
diff --git a/updater_sample/res/raw/sample.json b/updater_sample/res/raw/sample.json
index b6f4cdc..7ac8ffa 100644
--- a/updater_sample/res/raw/sample.json
+++ b/updater_sample/res/raw/sample.json
@@ -8,6 +8,7 @@
     "ab_streaming_metadata": {
         "__": "streaming_metadata is required only for streaming update",
         "__property_files": "name, offset and size of files",
+        "__authorization": "it will be sent to OTA package server as value of HTTP header - Authorization",
         "property_files": [
             {
                 "__filename": "name of the file in package",
@@ -17,6 +18,7 @@
                 "offset": 531,
                 "size": 5012323
             }
-        ]
+        ],
+        "authorization": "Basic my-secret-token"
     }
 }
diff --git a/updater_sample/src/com/example/android/systemupdatersample/UpdateConfig.java b/updater_sample/src/com/example/android/systemupdatersample/UpdateConfig.java
index 1851724..b08bfd0 100644
--- a/updater_sample/src/com/example/android/systemupdatersample/UpdateConfig.java
+++ b/updater_sample/src/com/example/android/systemupdatersample/UpdateConfig.java
@@ -25,6 +25,7 @@
 
 import java.io.File;
 import java.io.Serializable;
+import java.util.Optional;
 
 /**
  * An update description. It will be parsed from JSON, which is intended to
@@ -78,7 +79,9 @@
                         p.getLong("offset"),
                         p.getLong("size"));
             }
-            c.mAbStreamingMetadata = new StreamingMetadata(propertyFiles);
+            c.mAbStreamingMetadata = new StreamingMetadata(
+                    propertyFiles,
+                    meta.getString("authorization_token"));
         }
         c.mRawJson = json;
         return c;
@@ -178,17 +181,23 @@
         /** defines beginning of update data in archive */
         private PackageFile[] mPropertyFiles;
 
-        public StreamingMetadata() {
-            mPropertyFiles = new PackageFile[0];
-        }
+        /** SystemUpdaterSample receives the authorization token from the OTA server, in addition
+         * to the package URL. It passes on the info to update_engine, so that the latter can
+         * fetch the data from the package server directly with the token. */
+        private String mAuthorization;
 
-        public StreamingMetadata(PackageFile[] propertyFiles) {
+        public StreamingMetadata(PackageFile[] propertyFiles, String authorization) {
             this.mPropertyFiles = propertyFiles;
+            this.mAuthorization = authorization;
         }
 
         public PackageFile[] getPropertyFiles() {
             return mPropertyFiles;
         }
+
+        public Optional<String> getAuthorization() {
+            return Optional.of(mAuthorization);
+        }
     }
 
     /**
@@ -224,7 +233,6 @@
         public long getSize() {
             return mSize;
         }
-
     }
 
 }
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 359e2b1..1708256 100644
--- a/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java
+++ b/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java
@@ -41,6 +41,7 @@
 import com.example.android.systemupdatersample.util.UpdateEngineStatuses;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -51,6 +52,10 @@
 
     private static final String TAG = "MainActivity";
 
+    /** HTTP Header: User-Agent; it will be sent to the server when streaming the payload. */
+    private static final String HTTP_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
+            + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36";
+
     private TextView mTextViewBuild;
     private Spinner mSpinnerConfigs;
     private TextView mTextViewConfigsDirHint;
@@ -295,12 +300,17 @@
                         .show();
                 return;
             }
-            updateEngineApplyPayload(payload);
+            updateEngineApplyPayload(payload, null);
         } else {
             Log.d(TAG, "Starting PrepareStreamingService");
             PrepareStreamingService.startService(this, config, (code, payloadSpec) -> {
                 if (code == PrepareStreamingService.RESULT_CODE_SUCCESS) {
-                    updateEngineApplyPayload(payloadSpec);
+                    List<String> extraProperties = new ArrayList<>();
+                    extraProperties.add("USER_AGENT=" + HTTP_USER_AGENT);
+                    config.getStreamingMetadata()
+                            .getAuthorization()
+                            .ifPresent(s -> extraProperties.add("AUTHORIZATION=" + s));
+                    updateEngineApplyPayload(payloadSpec, extraProperties);
                 } else {
                     Log.e(TAG, "PrepareStreamingService failed, result code is " + code);
                     Toast.makeText(
@@ -317,14 +327,21 @@
      *
      * UpdateEngine works asynchronously. This method doesn't wait until
      * end of the update.
+     *
+     * @param payloadSpec contains url, offset and size to {@code PAYLOAD_BINARY_FILE_NAME}
+     * @param extraProperties additional properties to pass to {@link UpdateEngine#applyPayload}
      */
-    private void updateEngineApplyPayload(PayloadSpec payloadSpec) {
+    private void updateEngineApplyPayload(PayloadSpec payloadSpec, List<String> extraProperties) {
+        ArrayList<String> properties = new ArrayList<>(payloadSpec.getProperties());
+        if (extraProperties != null) {
+            properties.addAll(extraProperties);
+        }
         try {
             mUpdateEngine.applyPayload(
                     payloadSpec.getUrl(),
                     payloadSpec.getOffset(),
                     payloadSpec.getSize(),
-                    payloadSpec.getProperties().toArray(new String[0]));
+                    properties.toArray(new String[0]));
         } catch (Exception e) {
             Log.e(TAG, "UpdateEngine failed to apply the update", e);
             Toast.makeText(