Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.systemupdatersample.util;
     18 
     19 import com.example.android.systemupdatersample.PayloadSpec;
     20 
     21 import java.io.BufferedReader;
     22 import java.io.File;
     23 import java.io.IOException;
     24 import java.io.InputStream;
     25 import java.io.InputStreamReader;
     26 import java.nio.file.Files;
     27 import java.util.ArrayList;
     28 import java.util.Arrays;
     29 import java.util.Enumeration;
     30 import java.util.List;
     31 import java.util.zip.ZipEntry;
     32 import java.util.zip.ZipFile;
     33 
     34 /** The helper class that creates {@link PayloadSpec}. */
     35 public class PayloadSpecs {
     36 
     37     public PayloadSpecs() {}
     38 
     39     /**
     40      * The payload PAYLOAD_ENTRY is stored in the zip package to comply with the Android OTA package
     41      * format. We want to find out the offset of the entry, so that we can pass it over to the A/B
     42      * updater without making an extra copy of the payload.
     43      *
     44      * <p>According to Android docs, the entries are listed in the order in which they appear in the
     45      * zip file. So we enumerate the entries to identify the offset of the payload file.
     46      * http://developer.android.com/reference/java/util/zip/ZipFile.html#entries()
     47      */
     48     public PayloadSpec forNonStreaming(File packageFile) throws IOException {
     49         boolean payloadFound = false;
     50         long payloadOffset = 0;
     51         long payloadSize = 0;
     52 
     53         List<String> properties = new ArrayList<>();
     54         try (ZipFile zip = new ZipFile(packageFile)) {
     55             Enumeration<? extends ZipEntry> entries = zip.entries();
     56             long offset = 0;
     57             while (entries.hasMoreElements()) {
     58                 ZipEntry entry = entries.nextElement();
     59                 String name = entry.getName();
     60                 // Zip local file header has 30 bytes + filename + sizeof extra field.
     61                 // https://en.wikipedia.org/wiki/Zip_(file_format)
     62                 long extraSize = entry.getExtra() == null ? 0 : entry.getExtra().length;
     63                 offset += 30 + name.length() + extraSize;
     64 
     65                 if (entry.isDirectory()) {
     66                     continue;
     67                 }
     68 
     69                 long length = entry.getCompressedSize();
     70                 if (PackageFiles.PAYLOAD_BINARY_FILE_NAME.equals(name)) {
     71                     if (entry.getMethod() != ZipEntry.STORED) {
     72                         throw new IOException("Invalid compression method.");
     73                     }
     74                     payloadFound = true;
     75                     payloadOffset = offset;
     76                     payloadSize = length;
     77                 } else if (PackageFiles.PAYLOAD_PROPERTIES_FILE_NAME.equals(name)) {
     78                     InputStream inputStream = zip.getInputStream(entry);
     79                     if (inputStream != null) {
     80                         BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
     81                         String line;
     82                         while ((line = br.readLine()) != null) {
     83                             properties.add(line);
     84                         }
     85                     }
     86                 }
     87                 offset += length;
     88             }
     89         }
     90 
     91         if (!payloadFound) {
     92             throw new IOException("Failed to find payload entry in the given package.");
     93         }
     94         return PayloadSpec.newBuilder()
     95                         .url("file://" + packageFile.getAbsolutePath())
     96                         .offset(payloadOffset)
     97                         .size(payloadSize)
     98                         .properties(properties)
     99                         .build();
    100     }
    101 
    102     /**
    103      * Creates a {@link PayloadSpec} for streaming update.
    104      */
    105     public PayloadSpec forStreaming(String updateUrl,
    106                                            long offset,
    107                                            long size,
    108                                            File propertiesFile) throws IOException {
    109         return PayloadSpec.newBuilder()
    110                 .url(updateUrl)
    111                 .offset(offset)
    112                 .size(size)
    113                 .properties(Files.readAllLines(propertiesFile.toPath()))
    114                 .build();
    115     }
    116 
    117     /**
    118      * Converts an {@link PayloadSpec} to a string.
    119      */
    120     public String specToString(PayloadSpec payloadSpec) {
    121         return "<PayloadSpec url=" + payloadSpec.getUrl()
    122                 + ", offset=" + payloadSpec.getOffset()
    123                 + ", size=" + payloadSpec.getSize()
    124                 + ", properties=" + Arrays.toString(
    125                         payloadSpec.getProperties().toArray(new String[0]))
    126                 + ">";
    127     }
    128 
    129 }
    130