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