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