Home | History | Annotate | Download | only in aapt2
      1 /*
      2  * Copyright (C) 2016 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 #include "LoadedApk.h"
     18 
     19 #include "ResourceValues.h"
     20 #include "ValueVisitor.h"
     21 #include "format/Archive.h"
     22 #include "format/binary/TableFlattener.h"
     23 #include "format/binary/XmlFlattener.h"
     24 #include "format/proto/ProtoDeserialize.h"
     25 #include "format/proto/ProtoSerialize.h"
     26 #include "io/BigBufferStream.h"
     27 #include "io/Util.h"
     28 #include "xml/XmlDom.h"
     29 
     30 using ::aapt::io::IFile;
     31 using ::aapt::io::IFileCollection;
     32 using ::aapt::xml::XmlResource;
     33 using ::android::StringPiece;
     34 using ::std::unique_ptr;
     35 
     36 namespace aapt {
     37 
     38 std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(const StringPiece& path, IDiagnostics* diag) {
     39   Source source(path);
     40   std::string error;
     41   std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::Create(path, &error);
     42   if (apk == nullptr) {
     43     diag->Error(DiagMessage(path) << "failed opening zip: " << error);
     44     return {};
     45   }
     46 
     47   ApkFormat apkFormat = DetermineApkFormat(apk.get());
     48   switch (apkFormat) {
     49     case ApkFormat::kBinary:
     50       return LoadBinaryApkFromFileCollection(source, std::move(apk), diag);
     51     case ApkFormat::kProto:
     52       return LoadProtoApkFromFileCollection(source, std::move(apk), diag);
     53     default:
     54       diag->Error(DiagMessage(path) << "could not identify format of APK");
     55       return {};
     56   }
     57 }
     58 
     59 std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection(
     60     const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) {
     61   std::unique_ptr<ResourceTable> table;
     62 
     63   io::IFile* table_file = collection->FindFile(kProtoResourceTablePath);
     64   if (table_file != nullptr) {
     65     pb::ResourceTable pb_table;
     66     std::unique_ptr<io::InputStream> in = table_file->OpenInputStream();
     67     if (in == nullptr) {
     68       diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath);
     69       return {};
     70     }
     71 
     72     io::ZeroCopyInputAdaptor adaptor(in.get());
     73     if (!pb_table.ParseFromZeroCopyStream(&adaptor)) {
     74       diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath);
     75       return {};
     76     }
     77 
     78     std::string error;
     79     table = util::make_unique<ResourceTable>();
     80     if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) {
     81       diag->Error(DiagMessage(source)
     82                   << "failed to deserialize " << kProtoResourceTablePath << ": " << error);
     83       return {};
     84     }
     85   }
     86 
     87   io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath);
     88   if (manifest_file == nullptr) {
     89     diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath);
     90     return {};
     91   }
     92 
     93   std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
     94   if (manifest_in == nullptr) {
     95     diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath);
     96     return {};
     97   }
     98 
     99   pb::XmlNode pb_node;
    100   io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
    101   if (!pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
    102     diag->Error(DiagMessage(source) << "failed to read proto " << kAndroidManifestPath);
    103     return {};
    104   }
    105 
    106   std::string error;
    107   std::unique_ptr<xml::XmlResource> manifest = DeserializeXmlResourceFromPb(pb_node, &error);
    108   if (manifest == nullptr) {
    109     diag->Error(DiagMessage(source)
    110                 << "failed to deserialize proto " << kAndroidManifestPath << ": " << error);
    111     return {};
    112   }
    113   return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table),
    114                                       std::move(manifest), ApkFormat::kProto);
    115 }
    116 
    117 std::unique_ptr<LoadedApk> LoadedApk::LoadBinaryApkFromFileCollection(
    118     const Source& source, unique_ptr<io::IFileCollection> collection, IDiagnostics* diag) {
    119   std::unique_ptr<ResourceTable> table;
    120 
    121   io::IFile* table_file = collection->FindFile(kApkResourceTablePath);
    122   if (table_file != nullptr) {
    123     table = util::make_unique<ResourceTable>();
    124     std::unique_ptr<io::IData> data = table_file->OpenAsData();
    125     if (data == nullptr) {
    126       diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath);
    127       return {};
    128     }
    129     BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(),
    130                                 collection.get());
    131     if (!parser.Parse()) {
    132       return {};
    133     }
    134   }
    135 
    136   io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath);
    137   if (manifest_file == nullptr) {
    138     diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath);
    139     return {};
    140   }
    141 
    142   std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
    143   if (manifest_data == nullptr) {
    144     diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath);
    145     return {};
    146   }
    147 
    148   std::string error;
    149   std::unique_ptr<xml::XmlResource> manifest =
    150       xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
    151   if (manifest == nullptr) {
    152     diag->Error(DiagMessage(source)
    153                 << "failed to parse binary " << kAndroidManifestPath << ": " << error);
    154     return {};
    155   }
    156   return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table),
    157                                       std::move(manifest), ApkFormat::kBinary);
    158 }
    159 
    160 bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
    161                                IArchiveWriter* writer) {
    162   FilterChain empty;
    163   return WriteToArchive(context, table_.get(), options, &empty, writer);
    164 }
    165 
    166 bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table,
    167                                const TableFlattenerOptions& options, FilterChain* filters,
    168                                IArchiveWriter* writer, XmlResource* manifest) {
    169   std::set<std::string> referenced_resources;
    170   // List the files being referenced in the resource table.
    171   for (auto& pkg : split_table->packages) {
    172     for (auto& type : pkg->types) {
    173       for (auto& entry : type->entries) {
    174         for (auto& config_value : entry->values) {
    175           FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
    176           if (file_ref) {
    177             referenced_resources.insert(*file_ref->path);
    178           }
    179         }
    180       }
    181     }
    182   }
    183 
    184   std::unique_ptr<io::IFileCollectionIterator> iterator = apk_->Iterator();
    185   while (iterator->HasNext()) {
    186     io::IFile* file = iterator->Next();
    187 
    188     std::string path = file->GetSource().path;
    189     // The name of the path has the format "<zip-file-name>@<path-to-file>".
    190     path = path.substr(path.find('@') + 1);
    191 
    192     // Skip resources that are not referenced if requested.
    193     if (path.find("res/") == 0 && referenced_resources.find(path) == referenced_resources.end()) {
    194       if (context->IsVerbose()) {
    195         context->GetDiagnostics()->Note(DiagMessage()
    196                                         << "Removing resource '" << path << "' from APK.");
    197       }
    198       continue;
    199     }
    200 
    201     if (!filters->Keep(path)) {
    202       if (context->IsVerbose()) {
    203         context->GetDiagnostics()->Note(DiagMessage() << "Filtered '" << path << "' from APK.");
    204       }
    205       continue;
    206     }
    207 
    208     // The resource table needs to be re-serialized since it might have changed.
    209     if (format_ == ApkFormat::kBinary && path == kApkResourceTablePath) {
    210       BigBuffer buffer(4096);
    211       // TODO(adamlesinski): How to determine if there were sparse entries (and if to encode
    212       // with sparse entries) b/35389232.
    213       TableFlattener flattener(options, &buffer);
    214       if (!flattener.Consume(context, split_table)) {
    215         return false;
    216       }
    217 
    218       io::BigBufferInputStream input_stream(&buffer);
    219       if (!io::CopyInputStreamToArchive(context,
    220                                         &input_stream,
    221                                         path,
    222                                         ArchiveEntry::kAlign,
    223                                         writer)) {
    224         return false;
    225       }
    226     } else if (format_ == ApkFormat::kProto && path == kProtoResourceTablePath) {
    227       pb::ResourceTable pb_table;
    228       SerializeTableToPb(*split_table, &pb_table, context->GetDiagnostics());
    229       if (!io::CopyProtoToArchive(context,
    230                                   &pb_table,
    231                                   path,
    232                                   ArchiveEntry::kAlign, writer)) {
    233         return false;
    234       }
    235     } else if (manifest != nullptr && path == "AndroidManifest.xml") {
    236       BigBuffer buffer(8192);
    237       XmlFlattenerOptions xml_flattener_options;
    238       xml_flattener_options.use_utf16 = true;
    239       XmlFlattener xml_flattener(&buffer, xml_flattener_options);
    240       if (!xml_flattener.Consume(context, manifest)) {
    241         context->GetDiagnostics()->Error(DiagMessage(path) << "flattening failed");
    242         return false;
    243       }
    244 
    245       uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
    246       io::BigBufferInputStream manifest_buffer_in(&buffer);
    247       if (!io::CopyInputStreamToArchive(context, &manifest_buffer_in, path, compression_flags,
    248                                         writer)) {
    249         return false;
    250       }
    251     } else {
    252       if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
    253         return false;
    254       }
    255     }
    256   }
    257   return true;
    258 }
    259 
    260 ApkFormat LoadedApk::DetermineApkFormat(io::IFileCollection* apk) {
    261   if (apk->FindFile(kApkResourceTablePath) != nullptr) {
    262     return ApkFormat::kBinary;
    263   } else if (apk->FindFile(kProtoResourceTablePath) != nullptr) {
    264     return ApkFormat::kProto;
    265   } else {
    266     // If the resource table is not present, attempt to read the manifest.
    267     io::IFile* manifest_file = apk->FindFile(kAndroidManifestPath);
    268     if (manifest_file == nullptr) {
    269       return ApkFormat::kUnknown;
    270     }
    271 
    272     // First try in proto format.
    273     std::unique_ptr<io::InputStream> manifest_in = manifest_file->OpenInputStream();
    274     if (manifest_in != nullptr) {
    275       pb::XmlNode pb_node;
    276       io::ZeroCopyInputAdaptor manifest_adaptor(manifest_in.get());
    277       if (pb_node.ParseFromZeroCopyStream(&manifest_adaptor)) {
    278         return ApkFormat::kProto;
    279       }
    280     }
    281 
    282     // If it didn't work, try in binary format.
    283     std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
    284     if (manifest_data != nullptr) {
    285       std::string error;
    286       std::unique_ptr<xml::XmlResource> manifest =
    287           xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
    288       if (manifest != nullptr) {
    289         return ApkFormat::kBinary;
    290       }
    291     }
    292 
    293     return ApkFormat::kUnknown;
    294   }
    295 }
    296 
    297 }  // namespace aapt
    298