Fix long paths on Windows

util::mkdirs iteratively creates each directory of a specified path. For
windows, Calling mkdir on only the drive letter or on the extended path
prefix (\?\\) will result in an error. Start after the long path prefix
and the drive letter.

This also changes AAPT2 to use AssetMaanager2 to retrieve symbols from
the symbol table. AssetManager2's zip library uses _wopen to open
windows files.

Bug:123251200
Test: aapt2_tests.exe
Change-Id: I26169d83b22d441485de3c49d63a6c4ed710e292
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 0032960..ffc1a92 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -44,6 +44,7 @@
 
 Maybe<ResourceName> ToResourceName(
     const android::ResTable::resource_name& name_in) {
+  // TODO: Remove this when ResTable and AssetManager(1) are removed from AAPT2
   ResourceName name_out;
   if (!name_in.package) {
     return {};
@@ -79,6 +80,41 @@
   return name_out;
 }
 
+Maybe<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in) {
+  ResourceName name_out;
+  if (!name_in.package) {
+    return {};
+  }
+
+  name_out.package = std::string(name_in.package, name_in.package_len);
+
+  const ResourceType* type;
+  if (name_in.type16) {
+    type = ParseResourceType(
+        util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len)));
+  } else if (name_in.type) {
+    type = ParseResourceType(StringPiece(name_in.type, name_in.type_len));
+  } else {
+    return {};
+  }
+
+  if (!type) {
+    return {};
+  }
+
+  name_out.type = *type;
+
+  if (name_in.entry16) {
+    name_out.entry =
+        util::Utf16ToUtf8(StringPiece16(name_in.entry16, name_in.entry_len));
+  } else if (name_in.entry) {
+    name_out.entry = std::string(name_in.entry, name_in.entry_len);
+  } else {
+    return {};
+  }
+  return name_out;
+}
+
 bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref,
                        bool* out_private) {
   if (str.empty()) {
diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h
index e282fd58..a8a3120 100644
--- a/tools/aapt2/ResourceUtils.h
+++ b/tools/aapt2/ResourceUtils.h
@@ -20,6 +20,7 @@
 #include <functional>
 #include <memory>
 
+#include "androidfw/AssetManager2.h"
 #include "androidfw/ConfigDescription.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/StringPiece.h"
@@ -78,6 +79,12 @@
     const android::ResTable::resource_name& name);
 
 /**
+ * Convert an android::AssetManager2::ResourceName to an aapt::ResourceName struct.
+ */
+Maybe<ResourceName> ToResourceName(
+    const android::AssetManager2::ResourceName& name_in);
+
+/**
  * Returns a boolean value if the string is equal to TRUE, true, True, FALSE,
  * false, or False.
  */
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 22edd2f..a7b8d25 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -717,28 +717,20 @@
   return true;
 }
 
-static int32_t FindFrameworkAssetManagerCookie(const android::AssetManager& assets) {
+static android::ApkAssetsCookie FindFrameworkAssetManagerCookie(
+    const android::AssetManager2& assets) {
   using namespace android;
 
   // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
   // we're looking for the first attribute resource in the system package.
-  const ResTable& table = assets.getResources(true);
-  Res_value val;
-  ssize_t idx = table.getResource(0x01010000, &val, true);
-  if (idx != NO_ERROR) {
-    // Try as a bag.
-    const ResTable::bag_entry* entry;
-    ssize_t cnt = table.lockBag(0x01010000, &entry);
-    if (cnt >= 0) {
-      idx = entry->stringBlock;
-    }
-    table.unlockBag(entry);
-  }
+  Res_value val{};
+  ResTable_config config{};
+  uint32_t type_spec_flags;
+  ApkAssetsCookie idx = assets.GetResource(0x01010000, true /** may_be_bag */,
+                                           0 /** density_override */, &val, &config,
+                                           &type_spec_flags);
 
-  if (idx < 0) {
-    return 0;
-  }
-  return table.getTableCookie(idx);
+  return idx;
 }
 
 class Linker {
@@ -750,17 +742,17 @@
         file_collection_(util::make_unique<io::FileCollection>()) {
   }
 
-  void ExtractCompileSdkVersions(android::AssetManager* assets) {
+  void ExtractCompileSdkVersions(android::AssetManager2* assets) {
     using namespace android;
 
-    int32_t cookie = FindFrameworkAssetManagerCookie(*assets);
-    if (cookie == 0) {
+    android::ApkAssetsCookie cookie = FindFrameworkAssetManagerCookie(*assets);
+    if (cookie == android::kInvalidCookie) {
       // No Framework assets loaded. Not a failure.
       return;
     }
 
     std::unique_ptr<Asset> manifest(
-        assets->openNonAsset(cookie, kAndroidManifestPath, Asset::AccessMode::ACCESS_BUFFER));
+        assets->OpenNonAsset(kAndroidManifestPath, cookie, Asset::AccessMode::ACCESS_BUFFER));
     if (manifest == nullptr) {
       // No errors.
       return;
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index a844a43..78e0074 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -20,9 +20,11 @@
 
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
-#include "androidfw/AssetManager.h"
+#include "androidfw/Asset.h"
+#include "androidfw/AssetManager2.h"
 #include "androidfw/ConfigDescription.h"
 #include "androidfw/ResourceTypes.h"
+#include "androidfw/ResourceUtils.h"
 
 #include "NameMangler.h"
 #include "Resource.h"
@@ -30,6 +32,7 @@
 #include "ValueVisitor.h"
 #include "util/Util.h"
 
+using ::android::ApkAssets;
 using ::android::ConfigDescription;
 using ::android::StringPiece;
 using ::android::StringPiece16;
@@ -214,51 +217,75 @@
 }
 
 bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) {
-  int32_t cookie = 0;
-  return assets_.addAssetPath(android::String8(path.data(), path.size()), &cookie);
+  if (std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path.data())) {
+    apk_assets_.push_back(std::move(apk));
+
+    std::vector<const ApkAssets*> apk_assets;
+    for (const std::unique_ptr<const ApkAssets>& apk_asset : apk_assets_) {
+      apk_assets.push_back(apk_asset.get());
+    }
+
+    asset_manager_.SetApkAssets(apk_assets, true /* invalidate_caches */,
+                                false /* filter_incompatible_configs */);
+    return true;
+  }
+  return false;
 }
 
 std::map<size_t, std::string> AssetManagerSymbolSource::GetAssignedPackageIds() const {
   std::map<size_t, std::string> package_map;
-  const android::ResTable& table = assets_.getResources(false);
-  const size_t package_count = table.getBasePackageCount();
-  for (size_t i = 0; i < package_count; i++) {
-    package_map[table.getBasePackageId(i)] =
-        util::Utf16ToUtf8(android::StringPiece16(table.getBasePackageName(i).string()));
-  }
+  asset_manager_.ForEachPackage([&package_map](const std::string& name, uint8_t id) -> bool {
+    package_map.insert(std::make_pair(id, name));
+    return true;
+  });
+
   return package_map;
 }
 
 bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
-  return assets_.getResources(false).isPackageDynamic(packageId);
+  if (packageId == 0) {
+    return true;
+  }
+
+  for (const std::unique_ptr<const ApkAssets>& assets : apk_assets_) {
+    for (const std::unique_ptr<const android::LoadedPackage>& loaded_package
+         : assets->GetLoadedArsc()->GetPackages()) {
+      if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) {
+        return true;
+      }
+    }
+  }
+
+  return false;
 }
 
 static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
-    const android::ResTable& table, ResourceId id) {
-  // Try as a bag.
-  const android::ResTable::bag_entry* entry;
-  ssize_t count = table.lockBag(id.id, &entry);
-  if (count < 0) {
-    table.unlockBag(entry);
+    android::AssetManager2& am, ResourceId id) {
+  if (am.GetApkAssets().empty()) {
+    return {};
+  }
+
+  const android::ResolvedBag* bag = am.GetBag(id.id);
+  if (bag == nullptr) {
     return nullptr;
   }
 
   // We found a resource.
   std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(id);
 
-  // Check to see if it is an attribute.
-  for (size_t i = 0; i < (size_t)count; i++) {
-    if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
-      s->attribute = std::make_shared<Attribute>(entry[i].map.value.data);
+  const size_t count = bag->entry_count;
+  for (uint32_t i = 0; i < count; i++) {
+    if (bag->entries[i].key == android::ResTable_map::ATTR_TYPE) {
+      s->attribute = std::make_shared<Attribute>(bag->entries[i].value.data);
       break;
     }
   }
 
   if (s->attribute) {
-    for (size_t i = 0; i < (size_t)count; i++) {
-      const android::ResTable_map& map_entry = entry[i].map;
-      if (Res_INTERNALID(map_entry.name.ident)) {
-        switch (map_entry.name.ident) {
+    for (size_t i = 0; i < count; i++) {
+      const android::ResolvedBag::Entry& map_entry = bag->entries[i];
+      if (Res_INTERNALID(map_entry.key)) {
+        switch (map_entry.key) {
           case android::ResTable_map::ATTR_MIN:
             s->attribute->min_int = static_cast<int32_t>(map_entry.value.data);
             break;
@@ -269,74 +296,65 @@
         continue;
       }
 
-      android::ResTable::resource_name entry_name;
-      if (!table.getResourceName(map_entry.name.ident, false, &entry_name)) {
-        table.unlockBag(entry);
+      android::AssetManager2::ResourceName name;
+      if (!am.GetResourceName(map_entry.key, &name)) {
         return nullptr;
       }
 
-      Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(entry_name);
+      Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(name);
       if (!parsed_name) {
         return nullptr;
       }
 
       Attribute::Symbol symbol;
       symbol.symbol.name = parsed_name.value();
-      symbol.symbol.id = ResourceId(map_entry.name.ident);
+      symbol.symbol.id = ResourceId(map_entry.key);
       symbol.value = map_entry.value.data;
       s->attribute->symbols.push_back(std::move(symbol));
     }
   }
-  table.unlockBag(entry);
+
   return s;
 }
 
 std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
     const ResourceName& name) {
-  const android::ResTable& table = assets_.getResources(false);
+  const std::string mangled_entry = NameMangler::MangleEntry(name.package, name.entry);
 
-  const std::u16string package16 = util::Utf8ToUtf16(name.package);
-  const std::u16string type16 = util::Utf8ToUtf16(to_string(name.type));
-  const std::u16string entry16 = util::Utf8ToUtf16(name.entry);
-  const std::u16string mangled_entry16 =
-      util::Utf8ToUtf16(NameMangler::MangleEntry(name.package, name.entry));
-
+  bool found = false;
+  ResourceId res_id = 0;
   uint32_t type_spec_flags;
-  ResourceId res_id;
 
   // There can be mangled resources embedded within other packages. Here we will
   // look into each package and look-up the mangled name until we find the resource.
-  const size_t count = table.getBasePackageCount();
-  for (size_t i = 0; i < count; i++) {
-    const android::String16 package_name = table.getBasePackageName(i);
-    StringPiece16 real_package16 = package16;
-    StringPiece16 real_entry16 = entry16;
-    std::u16string scratch_entry16;
-    if (StringPiece16(package_name) != package16) {
-      real_entry16 = mangled_entry16;
-      real_package16 = package_name.string();
+  asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool {
+    ResourceName real_name(name.package, name.type, name.entry);
+
+    if (package_name != name.package) {
+      real_name.entry = mangled_entry;
+      real_name.package = package_name;
     }
 
-    type_spec_flags = 0;
-    res_id = table.identifierForName(real_entry16.data(), real_entry16.size(), type16.data(),
-                                     type16.size(), real_package16.data(), real_package16.size(),
-                                     &type_spec_flags);
-    if (res_id.is_valid()) {
-      break;
+    res_id = asset_manager_.GetResourceId(real_name.to_string());
+    if (res_id.is_valid() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) {
+      found = true;
+      return false;
     }
-  }
 
-  if (!res_id.is_valid()) {
+    return true;
+  });
+
+  if (!found) {
     return {};
   }
 
   std::unique_ptr<SymbolTable::Symbol> s;
   if (name.type == ResourceType::kAttr) {
-    s = LookupAttributeInTable(table, res_id);
+    s = LookupAttributeInTable(asset_manager_, res_id);
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
     s->id = res_id;
-    s->is_dynamic = table.isResourceDynamic(res_id.id);
+    s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id());
   }
 
   if (s) {
@@ -346,13 +364,13 @@
   return {};
 }
 
-static Maybe<ResourceName> GetResourceName(const android::ResTable& table,
+static Maybe<ResourceName> GetResourceName(android::AssetManager2& am,
                                            ResourceId id) {
-  android::ResTable::resource_name res_name = {};
-  if (!table.getResourceName(id.id, true, &res_name)) {
+  android::AssetManager2::ResourceName name;
+  if (!am.GetResourceName(id.id, &name)) {
     return {};
   }
-  return ResourceUtils::ToResourceName(res_name);
+  return ResourceUtils::ToResourceName(name);
 }
 
 std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
@@ -361,22 +379,30 @@
     // Exit early and avoid the error logs from AssetManager.
     return {};
   }
-  const android::ResTable& table = assets_.getResources(false);
-  Maybe<ResourceName> maybe_name = GetResourceName(table, id);
+
+  if (apk_assets_.empty()) {
+    return {};
+  }
+
+  Maybe<ResourceName> maybe_name = GetResourceName(asset_manager_, id);
   if (!maybe_name) {
     return {};
   }
 
-  uint32_t type_spec_flags = 0;
-  table.getResourceFlags(id.id, &type_spec_flags);
 
+  uint32_t type_spec_flags = 0;
+  if (!asset_manager_.GetResourceFlags(id.id, &type_spec_flags)) {
+    return {};
+  }
+
+  ResourceName& name = maybe_name.value();
   std::unique_ptr<SymbolTable::Symbol> s;
-  if (maybe_name.value().type == ResourceType::kAttr) {
-    s = LookupAttributeInTable(table, id);
+  if (name.type == ResourceType::kAttr) {
+    s = LookupAttributeInTable(asset_manager_, id);
   } else {
     s = util::make_unique<SymbolTable::Symbol>();
     s->id = id;
-    s->is_dynamic = table.isResourceDynamic(id.id);
+    s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id());
   }
 
   if (s) {
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 2d8bd02..6997cd6 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -22,7 +22,8 @@
 #include <vector>
 
 #include "android-base/macros.h"
-#include "androidfw/AssetManager.h"
+#include "androidfw/Asset.h"
+#include "androidfw/AssetManager2.h"
 #include "utils/JenkinsHash.h"
 #include "utils/LruCache.h"
 
@@ -201,12 +202,13 @@
   std::unique_ptr<SymbolTable::Symbol> FindByReference(
       const Reference& ref) override;
 
-  android::AssetManager* GetAssetManager() {
-    return &assets_;
+  android::AssetManager2* GetAssetManager() {
+    return &asset_manager_;
   }
 
  private:
-  android::AssetManager assets_;
+  android::AssetManager2 asset_manager_;
+  std::vector<std::unique_ptr<const android::ApkAssets>> apk_assets_;
 
   DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource);
 };
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index 1f59d70..ddc2101 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -76,40 +76,54 @@
   EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
 }
 
-TEST(SymbolTableTest, FindByNameWhenSymbolIsMangledInResTable) {
+using SymbolTableTestFixture = CommandTestFixture;
+TEST_F(SymbolTableTestFixture, FindByNameWhenSymbolIsMangledInResTable) {
   using namespace android;
+  StdErrDiagnostics diag;
 
-  std::unique_ptr<IAaptContext> context =
-      test::ContextBuilder()
-          .SetCompilationPackage("com.android.app")
-          .SetPackageId(0x7f)
-          .SetPackageType(PackageType::kApp)
-          .SetMinSdkVersion(SDK_LOLLIPOP_MR1)
-          .SetNameManglerPolicy(NameManglerPolicy{"com.android.app"})
-          .Build();
+  // Create a static library.
+  const std::string static_lib_compiled_files_dir = GetTestPath("static-lib-compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+         R"(<?xml version="1.0" encoding="utf-8"?>
+         <resources>
+             <item type="id" name="foo"/>
+        </resources>)",
+         static_lib_compiled_files_dir, &diag));
 
-  // Create a ResourceTable with a mangled resource, simulating a static library being merged into
-  // the main application package.
-  std::unique_ptr<ResourceTable> table =
-      test::ResourceTableBuilder()
-          .AddSimple("com.android.app:id/" + NameMangler::MangleEntry("com.android.lib", "foo"),
-                     ResourceId(0x7f020000))
-          .AddSimple("com.android.app:id/bar", ResourceId(0x7f020001))
-          .Build();
+  const std::string static_lib_apk = GetTestPath("static_lib.apk");
+  std::vector<std::string> link_args = {
+      "--manifest", GetDefaultManifest("com.android.lib"),
+      "--min-sdk-version", "22",
+      "--static-lib",
+      "-o", static_lib_apk,
+  };
+  ASSERT_TRUE(Link(link_args, static_lib_compiled_files_dir, &diag));
 
-  BigBuffer buffer(1024u);
-  TableFlattener flattener({}, &buffer);
-  ASSERT_TRUE(flattener.Consume(context.get(), table.get()));
+  // Merge the static library into the main application package. The static library resources will
+  // be mangled with the library package name.
+  const std::string app_compiled_files_dir = GetTestPath("app-compiled");
+  ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+      R"(<?xml version="1.0" encoding="utf-8"?>
+         <resources>
+             <item type="id" name="bar"/>
+        </resources>)",
+        app_compiled_files_dir, &diag));
 
-  std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+  const std::string out_apk = GetTestPath("out.apk");
+  link_args = {
+      "--manifest", GetDefaultManifest("com.android.app"),
+      "--min-sdk-version", "22",
+      "-o", out_apk,
+      static_lib_apk
+  };
+  ASSERT_TRUE(Link(link_args, app_compiled_files_dir, &diag));
 
   // Construct the test AssetManager.
   auto asset_manager_source = util::make_unique<AssetManagerSymbolSource>();
-  ResTable& res_table = const_cast<ResTable&>(
-      asset_manager_source->GetAssetManager()->getResources(false /*required*/));
-  ASSERT_THAT(res_table.add(data.get(), buffer.size()), Eq(NO_ERROR));
+  asset_manager_source->AddAssetPath(out_apk);
 
-  SymbolTable symbol_table(context->GetNameMangler());
+  NameMangler name_mangler(NameManglerPolicy{"com.android.app"});
+  SymbolTable symbol_table(&name_mangler);
   symbol_table.AppendSource(std::move(asset_manager_source));
 
   EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index 3fcdfb7..a51b4a4 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -37,6 +37,8 @@
 
 namespace aapt {
 
+const char* CommandTestFixture::kDefaultPackageName = "com.aapt.command.test";
+
 void ClearDirectory(const android::StringPiece& path) {
   const std::string root_dir = path.to_string();
   std::unique_ptr<DIR, decltype(closedir)*> dir(opendir(root_dir.data()), closedir);
@@ -124,12 +126,12 @@
   return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
 }
 
-std::string CommandTestFixture::GetDefaultManifest() {
+std::string CommandTestFixture::GetDefaultManifest(const char* package_name) {
   const std::string manifest_file = GetTestPath("AndroidManifest.xml");
-  CHECK(WriteFile(manifest_file, R"(
+  CHECK(WriteFile(manifest_file, android::base::StringPrintf(R"(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.aapt.command.test">
-      </manifest>)"));
+          package="%s">
+      </manifest>)", package_name)));
   return manifest_file;
 }
 
diff --git a/tools/aapt2/test/Fixture.h b/tools/aapt2/test/Fixture.h
index 3079c75..fce2aeb 100644
--- a/tools/aapt2/test/Fixture.h
+++ b/tools/aapt2/test/Fixture.h
@@ -81,7 +81,7 @@
             IDiagnostics* diag);
 
   // Creates a minimal android manifest within the test directory and returns the file path.
-  std::string GetDefaultManifest();
+  std::string GetDefaultManifest(const char* package_name = kDefaultPackageName);
 
   // Returns pointer to data inside APK files
   std::unique_ptr<io::IData> OpenFileAsData(LoadedApk* apk,
@@ -91,6 +91,7 @@
   void AssertLoadXml(LoadedApk* apk, const io::IData* data,
                      android::ResXMLTree* out_tree);
 
+  static const char* kDefaultPackageName;
  private:
   DISALLOW_COPY_AND_ASSIGN(CommandTestFixture);
 };
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 604b257..5d57de6 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -102,12 +102,21 @@
 #endif
 
 bool mkdirs(const std::string& path) {
-  #ifdef _WIN32
-  // Start after the drive path if present. Calling mkdir with only the drive will cause an error.
-  size_t current_pos = 1u;
-  if (path.size() >= 3 && path[1] == ':' &&
-        (path[2] == '\\' || path[2] == '/')) {
-    current_pos = 3u;
+ #ifdef _WIN32
+  // Start after the long path prefix if present.
+  bool require_drive = false;
+  size_t current_pos = 0u;
+  if (util::StartsWith(path, R"(\\?\)")) {
+    require_drive = true;
+    current_pos = 4u;
+  }
+
+  // Start after the drive path if present.
+  if (path.size() >= 3 && path[current_pos + 1] == ':' &&
+       (path[current_pos + 2] == '\\' || path[current_pos + 2] == '/')) {
+    current_pos += 3u;
+  } else if (require_drive) {
+    return false;
   }
  #else
   // Start after the first character so that we don't consume the root '/'.
diff --git a/tools/aapt2/util/Files_test.cpp b/tools/aapt2/util/Files_test.cpp
index 202cc26..6c38080 100644
--- a/tools/aapt2/util/Files_test.cpp
+++ b/tools/aapt2/util/Files_test.cpp
@@ -19,6 +19,7 @@
 #include <sstream>
 
 #include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
 
 #include "test/Test.h"
 
@@ -65,5 +66,40 @@
   EXPECT_EQ(expected_path_, base);
 }
 
+#ifdef _WIN32
+TEST_F(FilesTest, WindowsMkdirsLongPath) {
+  // Creating directory paths longer than the Windows maximum path length (260 charatcers) should
+  // succeed.
+  const std::string kDirName = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+  const size_t kRecursiveDepth = 10u;
+
+  // Recursively create the test file path and clean up the created directories after the files have
+  // been created.
+  std::function<void(std::string, size_t)> CreateResursiveDirs =
+      [&kDirName, &CreateResursiveDirs](std::string current_path, const size_t n) -> void {
+    AppendPath(&current_path, kDirName);
+
+    if (n == 0) {
+      ASSERT_TRUE(file::mkdirs(current_path)) << "Failed to create path " << current_path;
+    } else {
+      CreateResursiveDirs(current_path, n - 1);
+    }
+
+    // Clean up the created directories.
+    _rmdir(current_path.data());
+  };
+
+  CreateResursiveDirs(
+      android::base::StringPrintf(R"(\\?\%s)", android::base::GetExecutableDirectory().data()),
+      kRecursiveDepth);
+}
+
+TEST_F(FilesTest, WindowsMkdirsLongPathMissingDrive) {
+  ASSERT_FALSE(file::mkdirs(R"(\\?\local\path\to\file)"));
+  ASSERT_FALSE(file::mkdirs(R"(\\?\:local\path\to\file)"));
+  ASSERT_FALSE(file::mkdirs(R"(\\?\\local\path\to\file)"));
+}
+#endif
+
 }  // namespace files
 }  // namespace aapt