Home | History | Annotate | Download | only in fakelibrary
      1 // Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
      2 // for details. All rights reserved. Use of this source code is governed by a
      3 // BSD-style license that can be found in the LICENSE file.
      4 
      5 package multidex004.fakelibrary;
      6 
      7 import multidex004.fakeframeworks.Context;
      8 
      9 import java.io.Closeable;
     10 import java.io.File;
     11 import java.io.FileFilter;
     12 import java.io.IOException;
     13 import java.util.List;
     14 
     15 /**
     16  * Exposes application secondary dex files as files in the application data
     17  * directory.
     18  */
     19 final class MultiDexExtractor {
     20 
     21     private static final String TAG = MultiDex.TAG;
     22 
     23     /**
     24      * We look for additional dex files named {@code classes2.dex},
     25      * {@code classes3.dex}, etc.
     26      */
     27     private static final String DEX_PREFIX = "classes";
     28     private static final String DEX_SUFFIX = ".dex";
     29 
     30     private static final String EXTRACTED_NAME_EXT = ".classes";
     31     private static final String EXTRACTED_SUFFIX = ".zip";
     32     private static final int MAX_EXTRACT_ATTEMPTS = 3;
     33 
     34     private static final String PREFS_FILE = "multidex.version";
     35     private static final String KEY_TIME_STAMP = "timestamp";
     36     private static final String KEY_CRC = "crc";
     37     private static final String KEY_DEX_NUMBER = "dex.number";
     38 
     39     /**
     40      * Size of reading buffers.
     41      */
     42     private static final int BUFFER_SIZE = 0x4000;
     43     /* Keep value away from 0 because it is a too probable time stamp value */
     44     private static final long NO_VALUE = -1L;
     45 
     46 
     47     private static List<File> loadExistingExtractions(Context context, File sourceApk, File dexDir)
     48             throws IOException {
     49 
     50         final String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
     51         int totalDexNumber = 1;
     52         final List<File> files = null;
     53 
     54         for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
     55             String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
     56             File extractedFile = new File(dexDir, fileName);
     57             if (extractedFile.isFile()) {
     58                 files.add(extractedFile);
     59             } else {
     60                 throw new IOException("Missing extracted secondary dex file '" +
     61                         extractedFile.getPath() + "'");
     62             }
     63         }
     64 
     65         return files;
     66     }
     67 
     68     private static long getTimeStamp(File archive) {
     69         long timeStamp = archive.lastModified();
     70         if (timeStamp == NO_VALUE) {
     71             // never return NO_VALUE
     72             timeStamp--;
     73         }
     74         return timeStamp;
     75     }
     76 
     77 
     78     private static long getZipCrc(File archive) throws IOException {
     79         long computedValue = ZipUtil.getZipCrc(archive);
     80         if (computedValue == NO_VALUE) {
     81             // never return NO_VALUE
     82             computedValue--;
     83         }
     84         return computedValue;
     85     }
     86 
     87     private static List<File> performExtractions(File sourceApk, File dexDir)
     88             throws IOException {
     89 
     90         final String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
     91 
     92         // Ensure that whatever deletions happen in prepareDexDir only happen if the zip that
     93         // contains a secondary dex file in there is not consistent with the latest apk.  Otherwise,
     94         // multi-process race conditions can cause a crash loop where one process deletes the zip
     95         // while another had created it.
     96         prepareDexDir(dexDir, extractedFilePrefix);
     97 
     98         List<File> files = null;
     99 
    100         return files;
    101     }
    102 
    103     /**
    104      * This removes any files that do not have the correct prefix.
    105      */
    106     private static void prepareDexDir(File dexDir, final String extractedFilePrefix)
    107             throws IOException {
    108         dexDir.mkdir();
    109         if (!dexDir.isDirectory()) {
    110             throw new IOException("Failed to create dex directory " + dexDir.getPath());
    111         }
    112 
    113         // Clean possible old files
    114         FileFilter filter = new FileFilter() {
    115 
    116             @Override
    117             public boolean accept(File pathname) {
    118                 return !pathname.getName().startsWith(extractedFilePrefix);
    119             }
    120         };
    121         File[] files = dexDir.listFiles(filter);
    122         if (files == null) {
    123             return;
    124         }
    125         for (File oldFile : files) {
    126             if (!oldFile.delete()) {
    127             } else {
    128             }
    129         }
    130     }
    131 
    132     /**
    133      * Closes the given {@code Closeable}. Suppresses any IO exceptions.
    134      */
    135     private static void closeQuietly(Closeable closeable) {
    136         try {
    137             closeable.close();
    138         } catch (IOException e) {
    139         }
    140     }
    141 
    142 }
    143