Merge "Fix a bug that job might not be able to be started properly."
diff --git a/scripts/cargo2android.py b/scripts/cargo2android.py
index 730df5a..f6ab3ac 100755
--- a/scripts/cargo2android.py
+++ b/scripts/cargo2android.py
@@ -603,6 +603,8 @@
       self.write('    test_suites: ["general-tests"],')
       self.write('    auto_gen_config: true,')
     self.dump_edition_flags_libs()
+    if 'test' in self.crate_types and len(self.srcs) == 1:
+      self.dump_test_data()
     self.write('}')
 
   def dump_single_type_android_module(self):
@@ -659,8 +661,14 @@
       for apex in self.runner.args.apex_available:
         self.write('        "%s",' % apex)
       self.write('    ],')
+    if self.runner.args.vendor_available:
+      self.write('    vendor_available: true,')
+    if self.runner.args.vendor_ramdisk_available:
+      self.write('    vendor_ramdisk_available: true,')
     if self.runner.args.min_sdk_version and crate_type == 'lib':
       self.write('    min_sdk_version: "%s",' % self.runner.args.min_sdk_version)
+    if crate_type == 'test' and not self.default_srcs:
+      self.dump_test_data()
     if self.runner.args.add_module_block:
       with open(self.runner.args.add_module_block, 'r') as f:
         self.write('    %s,' % f.read().replace('\n', '\n    '))
@@ -694,6 +702,12 @@
     shared_libs = [lib for lib in self.shared_libs if not lib in self.runner.args.lib_blocklist]
     self.dump_android_property_list('shared_libs', '"lib%s"', shared_libs)
 
+  def dump_test_data(self):
+    data = [data for (name, data) in map(lambda kv: kv.split('=', 1), self.runner.args.test_data)
+            if self.srcs == [name]]
+    if data:
+      self.dump_android_property_list('data', '"%s"', data)
+
   def main_src_basename_path(self):
     return re.sub('/', '_', re.sub('.rs$', '', self.main_src))
 
@@ -1609,6 +1623,16 @@
       nargs='*',
       help='Mark the main library as apex_available with the given apexes.')
   parser.add_argument(
+      '--vendor-available',
+      action='store_true',
+      default=False,
+      help='Mark the main library as vendor_available.')
+  parser.add_argument(
+      '--vendor-ramdisk-available',
+      action='store_true',
+      default=False,
+      help='Mark the main library as vendor_ramdisk_available.')
+  parser.add_argument(
       '--force-rlib',
       action='store_true',
       default=False,
@@ -1619,6 +1643,12 @@
       default=[],
       help='Make the given libraries (without lib prefixes) whole_static_libs.')
   parser.add_argument(
+      '--test-data',
+      nargs='*',
+      default=[],
+      help=('Add the given file to the given test\'s data property. ' +
+            'Usage: test-path=data-path'))
+  parser.add_argument(
       '--dependency-blocklist',
       nargs='*',
       default=[],
diff --git a/tools/ota_analysis/src/components/BasicInfo.vue b/tools/ota_analysis/src/components/BasicInfo.vue
new file mode 100644
index 0000000..8a3a2ec
--- /dev/null
+++ b/tools/ota_analysis/src/components/BasicInfo.vue
@@ -0,0 +1,78 @@
+<template>
+  <h3>Basic infos</h3>
+  <div
+    v-if="zipFile"
+    v-bind="$attrs"
+  >
+    <ul class="align">
+      <li><strong> File name </strong> {{ zipFile.name }}</li>
+      <li><strong> File size </strong> {{ zipFile.size }} Bytes</li>
+      <li>
+        <strong> File last modified date </strong>
+        {{ zipFile.lastModifiedDate }}
+      </li>
+    </ul>
+  </div>
+  <div v-if="payload && payload.manifest">
+    <ul class="align">
+      <li>
+        <strong> Incremental </strong>
+        <!-- Check if the first partition is incremental or not -->
+        <span v-if="payload.manifest.partitions[0].oldPartitionInfo">
+          &#9989;
+        </span>
+        <span v-else> &#10060; </span>
+      </li>
+      <li>
+        <strong> Partial </strong>
+        <span v-if="payload.manifest.partialUpdate"> &#9989; </span>
+        <span v-else> &#10060; </span>
+      </li>
+      <li>
+        <strong> VAB </strong>
+        <span v-if="payload.manifest.dynamicPartitionMetadata.snapshotEnabled">
+          &#9989;
+        </span>
+        <span v-else> &#10060; </span>
+      </li>
+      <li>
+        <strong> VABC </strong>
+        <span v-if="payload.manifest.dynamicPartitionMetadata.vabcEnabled">
+          &#9989;
+        </span>
+        <span v-else> &#10060; </span>
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script>
+import { Payload } from '@/services/payload.js'
+
+export default {
+  props: {
+    zipFile: {
+      type: File,
+      required: true,
+    },
+    payload: {
+      type: Payload,
+      required: true,
+    },
+  },
+}
+</script>
+
+<style scoped>
+.align strong {
+  display: inline-block;
+  width: 50%;
+  position: relative;
+  padding-right: 10px; /* Ensures colon does not overlay the text */
+  text-align: right;
+}
+
+.align strong::after {
+  content: ':';
+}
+</style>
\ No newline at end of file
diff --git a/tools/ota_analysis/src/components/PartitionDetail.vue b/tools/ota_analysis/src/components/PartitionDetail.vue
index 6648378..6a64448 100644
--- a/tools/ota_analysis/src/components/PartitionDetail.vue
+++ b/tools/ota_analysis/src/components/PartitionDetail.vue
@@ -1,9 +1,48 @@
 <template>
-  <p
+  <h4> {{ partition.partitionName }} </h4>
+  <p v-if="partition.estimateCowSize">
+    <strong> Estimate COW Size: </strong> {{ partition.estimateCowSize }} Bytes
+  </p>
+  <p v-else>
+    <strong> Estimate COW Size: </strong> 0 Bytes
+  </p>
+  <div
     class="toggle"
-    @click="toggle()"
+    @click="toggle('showInfo')"
   >
-    Total Operations: {{ partition.operations.length }}
+    <h4> Partition Infos </h4>
+    <ul v-if="showInfo">
+      <li v-if="partition.oldPartitionInfo">
+        <strong>
+          Old Partition Size:
+        </strong>
+        {{ partition.oldPartitionInfo.size }} Bytes
+      </li>
+      <li v-if="partition.oldPartitionInfo">
+        <strong>
+          Old Partition Hash:
+        </strong>
+        {{ octToHex(partition.oldPartitionInfo.hash, false, 16) }}
+      </li>
+      <li>
+        <strong>
+          New Partition Size:
+        </strong>
+        {{ partition.newPartitionInfo.size }} Bytes
+      </li>
+      <li>
+        <strong>
+          New Partition Hash:
+        </strong>
+        {{ octToHex(partition.newPartitionInfo.hash, false, 16) }}
+      </li>
+    </ul>
+  </div>
+  <div
+    class="toggle"
+    @click="toggle('showOPs')"
+  >
+    <h4> Total Operations: {{ partition.operations.length }} </h4>
     <ul
       v-if="showOPs"
     >
@@ -17,11 +56,11 @@
         />
       </li>
     </ul>
-  </p>
+  </div>
 </template>
 
 <script>
-import { OpType } from '@/services/payload.js'
+import { OpType, octToHex } from '@/services/payload.js'
 import OperationDetail from '@/components/OperationDetail.vue'
 
 export default {
@@ -37,6 +76,7 @@
   data() {
     return {
       showOPs: false,
+      showInfo: false,
       opType: null,
     }
   },
@@ -44,9 +84,10 @@
     this.opType = new OpType()
   },
   methods: {
-    toggle() {
-      this.showOPs = !this.showOPs
+    toggle(key) {
+      this[key] = !this[key]
     },
+    octToHex: octToHex,
   },
 }
 </script>
@@ -55,7 +96,7 @@
 .toggle {
   display: block;
   cursor: pointer;
-  color: #00c255;
+  color: #762ace;
 }
 
 li {
diff --git a/tools/ota_analysis/src/components/PayloadDetail.vue b/tools/ota_analysis/src/components/PayloadDetail.vue
index ed71d5e..15bb937 100644
--- a/tools/ota_analysis/src/components/PayloadDetail.vue
+++ b/tools/ota_analysis/src/components/PayloadDetail.vue
@@ -1,15 +1,9 @@
 <template>
-  <div
-    v-if="zipFile"
+  <BasicInfo
+    :zipFile="zipFile"
+    :payload="payload"
     class="mb-5"
-  >
-    <h3>File infos</h3>
-    <ul>
-      <li>File name: {{ zipFile.name }}</li>
-      <li>File size: {{ zipFile.size }} Bytes</li>
-      <li>File last modified date: {{ zipFile.lastModifiedDate }}</li>
-    </ul>
-  </div>
+  />
   <v-divider />
   <div v-if="payload">
     <h3>Partition List</h3>
@@ -29,13 +23,6 @@
           shaped
           class="partial-info"
         >
-          <h4> {{ partition.partitionName }} </h4>
-          <p v-if="partition.estimateCowSize">
-            <strong> Estimate COW Size: </strong> {{ partition.estimateCowSize }} Bytes
-          </p>
-          <p v-else>
-            <strong> Estimate COW Size: </strong> 0 Bytes
-          </p>
           <PartitionDetail :partition="partition" />
         </v-card>
       </v-col>
@@ -55,11 +42,13 @@
 
 <script>
 import PartitionDetail from './PartitionDetail.vue'
-import { Payload } from '@/services/payload.js'
+import BasicInfo from '@/components/BasicInfo.vue'
+import { Payload, octToHex } from '@/services/payload.js'
 
 export default {
   components: {
     PartitionDetail,
+    BasicInfo,
   },
   props: {
     zipFile: {
@@ -75,17 +64,6 @@
     octToHex: octToHex,
   },
 }
-
-function octToHex(bufferArray) {
-  let hex_table = ''
-  for (let i = 0; i < bufferArray.length; i++) {
-    hex_table += bufferArray[i].toString(16) + ' '
-    if ((i + 1) % 16 == 0) {
-      hex_table += '\n'
-    }
-  }
-  return hex_table
-}
 </script>
 
 <style scoped>
@@ -94,6 +72,7 @@
   height: 200px;
   width: 100%;
   word-break: break-all;
+  text-align: center;
 }
 
 .partial-info {
diff --git a/tools/ota_analysis/src/services/payload.js b/tools/ota_analysis/src/services/payload.js
index 542a582..5fdb95f 100644
--- a/tools/ota_analysis/src/services/payload.js
+++ b/tools/ota_analysis/src/services/payload.js
@@ -144,4 +144,19 @@
       this.mapType.set(types[key], key)
     }
   }
+}
+
+export function octToHex(bufferArray, space = true, maxLine = 16) {
+  let hex_table = ''
+  for (let i = 0; i < bufferArray.length; i++) {
+    if (bufferArray[i].toString(16).length===2) {
+      hex_table += bufferArray[i].toString(16) + (space ? ' ' : '')
+    } else {
+      hex_table += '0' + bufferArray[i].toString(16) + (space ? ' ' : '')
+    }
+    if ((i + 1) % maxLine == 0) {
+      hex_table += '\n'
+    }
+  }
+  return hex_table
 }
\ No newline at end of file
diff --git a/tools/repo_pull/gerrit.py b/tools/repo_pull/gerrit.py
index 9679d9d..aeaba79 100755
--- a/tools/repo_pull/gerrit.py
+++ b/tools/repo_pull/gerrit.py
@@ -346,6 +346,11 @@
 
     raise ValueError('cannot find gerrit URL from manifest')
 
+def normalize_gerrit_name(gerrit):
+    """Strip the trailing slashes because Gerrit will return 404 when there are
+    redundant trailing slashes."""
+    return gerrit.rstrip('/')
+
 def _parse_args():
     """Parse command line options."""
     parser = argparse.ArgumentParser()
@@ -361,12 +366,13 @@
 
     return parser.parse_args()
 
-
 def main():
     """Main function"""
     args = _parse_args()
 
-    if not args.gerrit:
+    if args.gerrit:
+        args.gerrit = normalize_gerrit_name(args.gerrit)
+    else:
         try:
             args.gerrit = find_gerrit_name()
         # pylint: disable=bare-except
diff --git a/tools/repo_pull/repo_patch.py b/tools/repo_pull/repo_patch.py
index f97d211..55ce348 100755
--- a/tools/repo_pull/repo_patch.py
+++ b/tools/repo_pull/repo_patch.py
@@ -26,7 +26,8 @@
 import sys
 
 from gerrit import (
-    create_url_opener_from_args, find_gerrit_name, query_change_lists, get_patch
+    create_url_opener_from_args, find_gerrit_name, normalize_gerrit_name,
+    query_change_lists, get_patch
 )
 
 def _parse_args():
@@ -49,7 +50,9 @@
     """Main function"""
     args = _parse_args()
 
-    if not args.gerrit:
+    if args.gerrit:
+        args.gerrit = normalize_gerrit_name(args.gerrit)
+    else:
         try:
             args.gerrit = find_gerrit_name()
         # pylint: disable=bare-except
diff --git a/tools/repo_pull/repo_pull.py b/tools/repo_pull/repo_pull.py
index 157ede5..6c8c282 100755
--- a/tools/repo_pull/repo_pull.py
+++ b/tools/repo_pull/repo_pull.py
@@ -32,7 +32,8 @@
 import xml.dom.minidom
 
 from gerrit import (
-    create_url_opener_from_args, find_gerrit_name, query_change_lists, run
+    create_url_opener_from_args, find_gerrit_name, normalize_gerrit_name,
+    query_change_lists, run
 )
 from subprocess import PIPE
 
@@ -414,7 +415,9 @@
     """Main function"""
     args = _parse_args()
 
-    if not args.gerrit:
+    if args.gerrit:
+        args.gerrit = normalize_gerrit_name(args.gerrit)
+    else:
         try:
             args.gerrit = find_gerrit_name()
         # pylint: disable=bare-except
diff --git a/tools/repo_pull/repo_review.py b/tools/repo_pull/repo_review.py
index e62820f..d33f790 100755
--- a/tools/repo_pull/repo_review.py
+++ b/tools/repo_pull/repo_review.py
@@ -32,8 +32,8 @@
 
 from gerrit import (
     abandon, add_reviewers, create_url_opener_from_args, delete_reviewer,
-    delete_topic, find_gerrit_name, query_change_lists, restore, set_hashtags,
-    set_review, set_topic, submit
+    delete_topic, find_gerrit_name, normalize_gerrit_name, query_change_lists,
+    restore, set_hashtags, set_review, set_topic, submit
 )
 
 
@@ -191,7 +191,9 @@
     # Parse and check the command line options
     args = _parse_args()
 
-    if not args.gerrit:
+    if args.gerrit:
+        args.gerrit = normalize_gerrit_name(args.gerrit)
+    else:
         try:
             args.gerrit = find_gerrit_name()
         # pylint: disable=bare-except