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 "flatten/Archive.h"
     22 #include "flatten/TableFlattener.h"
     23 #include "io/BigBufferInputStream.h"
     24 #include "io/Util.h"
     25 
     26 namespace aapt {
     27 
     28 std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context,
     29                                                       const android::StringPiece& path) {
     30   Source source(path);
     31   std::string error;
     32   std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::Create(path, &error);
     33   if (!apk) {
     34     context->GetDiagnostics()->Error(DiagMessage(source) << error);
     35     return {};
     36   }
     37 
     38   io::IFile* file = apk->FindFile("resources.arsc");
     39   if (!file) {
     40     context->GetDiagnostics()->Error(DiagMessage(source) << "no resources.arsc found");
     41     return {};
     42   }
     43 
     44   std::unique_ptr<io::IData> data = file->OpenAsData();
     45   if (!data) {
     46     context->GetDiagnostics()->Error(DiagMessage(source) << "could not open resources.arsc");
     47     return {};
     48   }
     49 
     50   std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
     51   BinaryResourceParser parser(context, table.get(), source, data->data(), data->size(), apk.get());
     52   if (!parser.Parse()) {
     53     return {};
     54   }
     55   return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
     56 }
     57 
     58 bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
     59                                IArchiveWriter* writer) {
     60   FilterChain empty;
     61   return WriteToArchive(context, options, &empty, writer);
     62 }
     63 
     64 bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
     65                                FilterChain* filters, IArchiveWriter* writer) {
     66   std::set<std::string> referenced_resources;
     67   // List the files being referenced in the resource table.
     68   for (auto& pkg : table_->packages) {
     69     for (auto& type : pkg->types) {
     70       for (auto& entry : type->entries) {
     71         for (auto& config_value : entry->values) {
     72           FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
     73           if (file_ref) {
     74             referenced_resources.insert(*file_ref->path);
     75           }
     76         }
     77       }
     78     }
     79   }
     80 
     81   std::unique_ptr<io::IFileCollectionIterator> iterator = apk_->Iterator();
     82   while (iterator->HasNext()) {
     83     io::IFile* file = iterator->Next();
     84 
     85     std::string path = file->GetSource().path;
     86     // The name of the path has the format "<zip-file-name>@<path-to-file>".
     87     path = path.substr(path.find("@") + 1);
     88 
     89     // Skip resources that are not referenced if requested.
     90     if (path.find("res/") == 0 && referenced_resources.find(path) == referenced_resources.end()) {
     91       if (context->IsVerbose()) {
     92         context->GetDiagnostics()->Note(DiagMessage()
     93                                         << "Removing resource '" << path << "' from APK.");
     94       }
     95       continue;
     96     }
     97 
     98     if (!filters->Keep(path)) {
     99       if (context->IsVerbose()) {
    100         context->GetDiagnostics()->Note(DiagMessage() << "Filtered '" << path << "' from APK.");
    101       }
    102       continue;
    103     }
    104 
    105     // The resource table needs to be re-serialized since it might have changed.
    106     if (path == "resources.arsc") {
    107       BigBuffer buffer(4096);
    108       // TODO(adamlesinski): How to determine if there were sparse entries (and if to encode
    109       // with sparse entries) b/35389232.
    110       TableFlattener flattener(options, &buffer);
    111       if (!flattener.Consume(context, table_.get())) {
    112         return false;
    113       }
    114 
    115       io::BigBufferInputStream input_stream(&buffer);
    116       if (!io::CopyInputStreamToArchive(context, &input_stream, path, ArchiveEntry::kAlign,
    117                                         writer)) {
    118         return false;
    119       }
    120 
    121     } else {
    122       uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
    123       if (!io::CopyFileToArchive(context, file, path, compression_flags, writer)) {
    124         return false;
    125       }
    126     }
    127   }
    128   return true;
    129 }
    130 
    131 }  // namespace aapt
    132