Merge "Add support for analysis of OTA package." am: eed4369516
Original change: https://android-review.googlesource.com/c/platform/development/+/1748540 Change-Id: Id32fb4e3f7c3f7de1311bb33888690c61ad9905d
This commit is contained in:
@@ -1,17 +1,21 @@
|
|||||||
{
|
{
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"node": true
|
"node": true
|
||||||
},
|
},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"parser": "babel-eslint"
|
"parser": "babel-eslint"
|
||||||
},
|
},
|
||||||
"extends": [
|
"extends": [
|
||||||
"plugin:vue/recommended"
|
"plugin:vue/recommended"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
2
|
||||||
],
|
],
|
||||||
"rules": {
|
"vue/no-multiple-template-root": 0,
|
||||||
"indent": ["error", 2],
|
"vue/attribute-hyphenation": 0
|
||||||
"vue/no-multiple-template-root": 0
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
27
tools/otagui/package-lock.json
generated
27
tools/otagui/package-lock.json
generated
@@ -8,6 +8,7 @@
|
|||||||
"name": "OTA_GUI",
|
"name": "OTA_GUI",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@zip.js/zip.js": "^2.3.6",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"echarts": "^5.1.2",
|
"echarts": "^5.1.2",
|
||||||
"eslint-config-airbnb-base": "^14.2.1",
|
"eslint-config-airbnb-base": "^14.2.1",
|
||||||
@@ -2944,6 +2945,11 @@
|
|||||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@zip.js/zip.js": {
|
||||||
|
"version": "2.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.3.6.tgz",
|
||||||
|
"integrity": "sha512-VQE2MI7YChMmdeBN9CGuktE4Mws+gUGAjWGUwzoIsT/gNmI+7BK+qbArsC5RO/NXYKJ1pj2vzNSZCyUFhPdG1g=="
|
||||||
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "1.3.7",
|
"version": "1.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||||
@@ -2995,14 +3001,18 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "6.12.4",
|
"version": "6.12.6",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
"json-schema-traverse": "^0.4.1",
|
"json-schema-traverse": "^0.4.1",
|
||||||
"uri-js": "^4.2.2"
|
"uri-js": "^4.2.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ajv-errors": {
|
"node_modules/ajv-errors": {
|
||||||
@@ -18115,6 +18125,11 @@
|
|||||||
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@zip.js/zip.js": {
|
||||||
|
"version": "2.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.3.6.tgz",
|
||||||
|
"integrity": "sha512-VQE2MI7YChMmdeBN9CGuktE4Mws+gUGAjWGUwzoIsT/gNmI+7BK+qbArsC5RO/NXYKJ1pj2vzNSZCyUFhPdG1g=="
|
||||||
|
},
|
||||||
"accepts": {
|
"accepts": {
|
||||||
"version": "1.3.7",
|
"version": "1.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||||
@@ -18149,9 +18164,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ajv": {
|
"ajv": {
|
||||||
"version": "6.12.4",
|
"version": "6.12.6",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"fast-deep-equal": "^3.1.1",
|
"fast-deep-equal": "^3.1.1",
|
||||||
"fast-json-stable-stringify": "^2.0.0",
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@zip.js/zip.js": "^2.3.6",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"echarts": "^5.1.2",
|
"echarts": "^5.1.2",
|
||||||
"eslint-config-airbnb-base": "^14.2.1",
|
"eslint-config-airbnb-base": "^14.2.1",
|
||||||
|
|||||||
@@ -6,7 +6,10 @@
|
|||||||
</router-link> |
|
</router-link> |
|
||||||
<router-link :to="{ name: 'JobList' }">
|
<router-link :to="{ name: 'JobList' }">
|
||||||
Jobs Status
|
Jobs Status
|
||||||
</router-link>|
|
</router-link> |
|
||||||
|
<router-link :to="{ name: 'Analysis' }">
|
||||||
|
Analysis
|
||||||
|
</router-link> |
|
||||||
<router-link :to="{ name: 'About' }">
|
<router-link :to="{ name: 'About' }">
|
||||||
About
|
About
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|||||||
@@ -24,9 +24,6 @@
|
|||||||
import FormDate from '../services/FormDate.js'
|
import FormDate from '../services/FormDate.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
|
||||||
FormDate,
|
|
||||||
},
|
|
||||||
props: {
|
props: {
|
||||||
job: {
|
job: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
|||||||
66
tools/otagui/src/components/OperationDetail.vue
Normal file
66
tools/otagui/src/components/OperationDetail.vue
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<template>
|
||||||
|
{{ mapType.get(operation.type) }}
|
||||||
|
<p v-if="operation.dataOffset !== null">
|
||||||
|
Data offset: {{ operation.dataOffset }}
|
||||||
|
</p>
|
||||||
|
<p v-if="operation.dataLength !== null">
|
||||||
|
Data length: {{ operation.dataLength }}
|
||||||
|
</p>
|
||||||
|
<p v-if="operation.srcExtents !== null">
|
||||||
|
Source: {{ operation.srcExtents.length }} extents ({{ srcTotalBlocks }}
|
||||||
|
blocks)
|
||||||
|
<br>
|
||||||
|
{{ srcBlocks }}
|
||||||
|
</p>
|
||||||
|
<p v-if="operation.dstExtents !== null">
|
||||||
|
Destination: {{ operation.dstExtents.length }} extents ({{ dstTotalBlocks }}
|
||||||
|
blocks)
|
||||||
|
<br>
|
||||||
|
{{ dstBlocks }}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
operation: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
mapType: {
|
||||||
|
type: Map,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
srcTotalBlocks: null,
|
||||||
|
srcBlocks: null,
|
||||||
|
dstTotalBlocks: null,
|
||||||
|
dstBlocks: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (this.operation.srcExtents) {
|
||||||
|
this.srcTotalBlocks = numBlocks(this.operation.srcExtents)
|
||||||
|
this.srcBlocks = displayBlocks(this.operation.srcExtents)
|
||||||
|
}
|
||||||
|
if (this.operation.dstExtents) {
|
||||||
|
this.dstTotalBlocks = numBlocks(this.operation.dstExtents)
|
||||||
|
this.dstBlocks = displayBlocks(this.operation.dstExtents)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
function numBlocks(exts) {
|
||||||
|
const accumulator = (total, ext) => total + ext.numBlocks
|
||||||
|
return exts.reduce(accumulator, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayBlocks(exts) {
|
||||||
|
const accumulator = (total, ext) =>
|
||||||
|
total + '(' + ext.startBlock + ',' + ext.numBlocks + ')'
|
||||||
|
return exts.reduce(accumulator, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
60
tools/otagui/src/components/PartitionDetail.vue
Normal file
60
tools/otagui/src/components/PartitionDetail.vue
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<template>
|
||||||
|
<p
|
||||||
|
class="toggle"
|
||||||
|
@click="toggle()"
|
||||||
|
>
|
||||||
|
Total Operations: {{ partition.operations.length }}
|
||||||
|
<ul
|
||||||
|
v-if="showOPs"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
v-for="operation in partition.operations"
|
||||||
|
:key="operation.dataSha256Hash"
|
||||||
|
>
|
||||||
|
<OperationDetail
|
||||||
|
:operation="operation"
|
||||||
|
:mapType="opType.mapType"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { OpType } from '@/services/payload.js'
|
||||||
|
import OperationDetail from '@/components/OperationDetail.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
OperationDetail,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
partition: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showOPs: false,
|
||||||
|
opType: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.opType = new OpType()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggle() {
|
||||||
|
this.showOPs = !this.showOPs
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.toggle {
|
||||||
|
display: block;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #00c255;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
81
tools/otagui/src/components/PayloadDetail.vue
Normal file
81
tools/otagui/src/components/PayloadDetail.vue
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="zipFile">
|
||||||
|
<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>
|
||||||
|
<div v-if="payload">
|
||||||
|
<h3>Partition List</h3>
|
||||||
|
<ul v-if="payload.manifest">
|
||||||
|
<li
|
||||||
|
v-for="partition in payload.manifest.partitions"
|
||||||
|
:key="partition.partitionName"
|
||||||
|
>
|
||||||
|
<h4>{{ partition.partitionName }}</h4>
|
||||||
|
<p v-if="partition.estimateCowSize">
|
||||||
|
Estimate COW Size: {{ partition.estimateCowSize }} Bytes
|
||||||
|
</p>
|
||||||
|
<p v-else>
|
||||||
|
Estimate COW Size: 0 Bytes
|
||||||
|
</p>
|
||||||
|
<PartitionDetail :partition="partition" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Metadata Signature</h3>
|
||||||
|
<div
|
||||||
|
v-if="payload.metadata_signature"
|
||||||
|
class="signature"
|
||||||
|
>
|
||||||
|
<span style="white-space: pre-wrap">
|
||||||
|
{{ octToHex(payload.metadata_signature.signatures[0].data) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PartitionDetail from './PartitionDetail.vue'
|
||||||
|
import { Payload } from '@/services/payload.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
PartitionDetail,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
zipFile: {
|
||||||
|
type: File,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
payload: {
|
||||||
|
type: Payload,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
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>
|
||||||
|
.signature {
|
||||||
|
overflow: scroll;
|
||||||
|
height: 200px;
|
||||||
|
width: 100%;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,6 +3,7 @@ import JobList from '@/views/JobList.vue'
|
|||||||
import JobDetails from '@/views/JobDetails.vue'
|
import JobDetails from '@/views/JobDetails.vue'
|
||||||
import About from '@/views/About.vue'
|
import About from '@/views/About.vue'
|
||||||
import SimpleForm from '@/views/SimpleForm.vue'
|
import SimpleForm from '@/views/SimpleForm.vue'
|
||||||
|
import PackageAnalysis from '@/views/PackageAnalysis.vue'
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@@ -25,6 +26,11 @@ const routes = [
|
|||||||
path: '/create',
|
path: '/create',
|
||||||
name: 'Create',
|
name: 'Create',
|
||||||
component: SimpleForm
|
component: SimpleForm
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/analysis',
|
||||||
|
name: 'Analysis',
|
||||||
|
component: PackageAnalysis
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
131
tools/otagui/src/services/payload.js
Normal file
131
tools/otagui/src/services/payload.js
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
* @fileoverview Clss paypload is used to read in and
|
||||||
|
* parse the payload.bin file from a OTA.zip file.
|
||||||
|
* Class OpType creates a Map that can resolve the
|
||||||
|
* operation type.
|
||||||
|
* @package zip.js
|
||||||
|
* @package protobufjs
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as zip from '@zip.js/zip.js/dist/zip-full.min.js'
|
||||||
|
import { chromeos_update_engine as update_metadata_pb } from './update_metadata_pb.js'
|
||||||
|
|
||||||
|
const _MAGIC = 'CrAU'
|
||||||
|
const _VERSION_SIZE = 8
|
||||||
|
const _MANIFEST_LEN_SIZE = 8
|
||||||
|
const _METADATA_SIGNATURE_LEN_SIZE = 4
|
||||||
|
const _BRILLO_MAJOR_PAYLOAD_VERSION = 2
|
||||||
|
|
||||||
|
export class Payload {
|
||||||
|
/**
|
||||||
|
* This class parses the metadata of a OTA package.
|
||||||
|
* @param {File} file A OTA.zip file read from user's machine.
|
||||||
|
*/
|
||||||
|
constructor(file) {
|
||||||
|
this.packedFile = new zip.ZipReader(new zip.BlobReader(file))
|
||||||
|
this.cursor = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
async unzipPayload() {
|
||||||
|
let entries = await this.packedFile.getEntries()
|
||||||
|
this.payload = null
|
||||||
|
for (let entry of entries) {
|
||||||
|
if (entry.filename == 'payload.bin') {
|
||||||
|
//TODO: only read in the manifest instead of the whole payload
|
||||||
|
this.payload = await entry.getData(new zip.BlobWriter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!this.payload) {
|
||||||
|
alert('Please select a legit OTA package')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.buffer = await this.payload.arrayBuffer()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read in an integer from binary bufferArray.
|
||||||
|
* @param {Int} size the size of a integer being read in
|
||||||
|
* @return {Int} an integer.
|
||||||
|
*/
|
||||||
|
readInt(size) {
|
||||||
|
let view = new DataView(
|
||||||
|
this.buffer.slice(this.cursor, this.cursor + size))
|
||||||
|
this.cursor += size
|
||||||
|
switch (size) {
|
||||||
|
case 2:
|
||||||
|
return view.getUInt16(0)
|
||||||
|
case 4:
|
||||||
|
return view.getUint32(0)
|
||||||
|
case 8:
|
||||||
|
return Number(view.getBigUint64(0))
|
||||||
|
default:
|
||||||
|
throw 'Cannot read this integer with size ' + size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readHeader() {
|
||||||
|
let decoder = new TextDecoder()
|
||||||
|
try {
|
||||||
|
this.magic = decoder.decode(
|
||||||
|
this.buffer.slice(this.cursor, _MAGIC.length))
|
||||||
|
this.cursor += _MAGIC.length
|
||||||
|
if (this.magic != _MAGIC) {
|
||||||
|
alert('MAGIC is not correct, please double check.')
|
||||||
|
}
|
||||||
|
this.header_version = this.readInt(_VERSION_SIZE)
|
||||||
|
this.manifest_len = this.readInt(_MANIFEST_LEN_SIZE)
|
||||||
|
if (this.header_version == _BRILLO_MAJOR_PAYLOAD_VERSION) {
|
||||||
|
this.metadata_signature_len = this.readInt(_METADATA_SIGNATURE_LEN_SIZE)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read in the manifest in an OTA.zip file.
|
||||||
|
* The structure of the manifest can be found in:
|
||||||
|
* aosp/system/update_engine/update_metadata.proto
|
||||||
|
*/
|
||||||
|
readManifest() {
|
||||||
|
let manifest_raw = new Uint8Array(this.buffer.slice(
|
||||||
|
this.cursor, this.cursor + this.manifest_len
|
||||||
|
))
|
||||||
|
this.cursor += this.manifest_len
|
||||||
|
this.manifest = update_metadata_pb.DeltaArchiveManifest
|
||||||
|
.decode(manifest_raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
readSignature() {
|
||||||
|
let signature_raw = new Uint8Array(this.buffer.slice(
|
||||||
|
this.cursor, this.cursor + this.metadata_signature_len
|
||||||
|
))
|
||||||
|
this.cursor += this.metadata_signature_len
|
||||||
|
this.metadata_signature = update_metadata_pb.Signatures
|
||||||
|
.decode(signature_raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
await this.unzipPayload()
|
||||||
|
this.readHeader()
|
||||||
|
this.readManifest()
|
||||||
|
this.readSignature()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class OpType {
|
||||||
|
/**
|
||||||
|
* OpType.mapType create a map that could resolve the operation
|
||||||
|
* types. The operation types are encoded as numbers in
|
||||||
|
* update_metadata.proto and must be decoded before any usage.
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
let types = update_metadata_pb.InstallOperation.Type
|
||||||
|
this.mapType = new Map()
|
||||||
|
for (let key in types) {
|
||||||
|
this.mapType.set(types[key], key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,10 +38,14 @@ import JobConfiguration from '../components/JobConfiguration.vue'
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ApiService,
|
|
||||||
JobConfiguration,
|
JobConfiguration,
|
||||||
},
|
},
|
||||||
props: ['id'],
|
props: {
|
||||||
|
id: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const stderr = ref()
|
const stderr = ref()
|
||||||
const stdout = ref()
|
const stdout = ref()
|
||||||
|
|||||||
44
tools/otagui/src/views/PackageAnalysis.vue
Normal file
44
tools/otagui/src/views/PackageAnalysis.vue
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<BaseFile
|
||||||
|
label="Select an OTA package"
|
||||||
|
@file-select="unpackOTA"
|
||||||
|
/>
|
||||||
|
<PayloadDetail
|
||||||
|
v-if="zipFile && payload"
|
||||||
|
:zipFile="zipFile"
|
||||||
|
:payload="payload"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import BaseFile from '@/components/BaseFile.vue'
|
||||||
|
import PayloadDetail from '@/components/PayloadDetail.vue'
|
||||||
|
import { Payload } from '@/services/payload.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
BaseFile,
|
||||||
|
PayloadDetail,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
zipFile: null,
|
||||||
|
payload: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async unpackOTA(files) {
|
||||||
|
this.zipFile = files[0]
|
||||||
|
try {
|
||||||
|
this.payload = new Payload(this.zipFile)
|
||||||
|
await this.payload.init()
|
||||||
|
} catch (err) {
|
||||||
|
alert('Please check if this is a correct OTA package (.zip).')
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -112,7 +112,6 @@ export default {
|
|||||||
UploadFile,
|
UploadFile,
|
||||||
FileSelect,
|
FileSelect,
|
||||||
PartialCheckbox,
|
PartialCheckbox,
|
||||||
FormDate,
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user