Add wgpuAdapterGetInfo

Following up on a webgpu.h change[1], this CL adds support for
wgpuAdapterGetInfo and wgpuAdapterInfoFreeMembers to mimic the
GPUAdapter info attribute in the JavaScript API.

[1]: https://github.com/webgpu-native/webgpu-headers/pull/296

Bug: 335383516
Change-Id: Ic93bd8a45f5051330801fe5a84b3663eb1f9c2b2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/190380
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Fr <beaufort.francois@gmail.com>
diff --git a/src/dawn/dawn.json b/src/dawn/dawn.json
index a0a8c3d..be35ce2 100644
--- a/src/dawn/dawn.json
+++ b/src/dawn/dawn.json
@@ -132,6 +132,13 @@
                 ]
             },
             {
+                "name": "get info",
+                "returns": "status",
+                "args": [
+                    {"name": "info", "type": "adapter info", "annotation": "*"}
+                ]
+            },
+            {
                 "name": "get properties",
                 "returns": "status",
                 "args": [
@@ -199,6 +206,20 @@
             }
         ]
     },
+    "adapter info": {
+        "category": "structure",
+        "extensible": "out",
+        "members": [
+            {"name": "vendor", "type": "char", "annotation": "const*", "length": "strlen", "default": "nullptr"},
+            {"name": "architecture", "type": "char", "annotation": "const*", "length": "strlen", "default": "nullptr"},
+            {"name": "device", "type": "char", "annotation": "const*", "length": "strlen", "default": "nullptr"},
+            {"name": "description", "type": "char", "annotation": "const*", "length": "strlen", "default": "nullptr"},
+            {"name": "backend type", "type": "backend type"},
+            {"name": "adapter type", "type": "adapter type"},
+            {"name": "vendor ID", "type": "uint32_t"},
+            {"name": "device ID", "type": "uint32_t"}
+        ]
+    },
     "adapter properties": {
         "category": "structure",
         "extensible": "out",
diff --git a/src/dawn/dawn_wire.json b/src/dawn/dawn_wire.json
index 0664654..e797463 100644
--- a/src/dawn/dawn_wire.json
+++ b/src/dawn/dawn_wire.json
@@ -180,6 +180,7 @@
             { "name": "future", "type": "future" },
             { "name": "status", "type": "request adapter status" },
             { "name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true },
+            { "name": "info", "type": "adapter info", "annotation": "const*", "optional": "true" },
             { "name": "properties", "type": "adapter properties", "annotation": "const*", "optional": "true" },
             { "name": "limits", "type": "supported limits", "annotation": "const*", "optional": "true" },
             { "name": "features count", "type": "uint32_t"},
@@ -208,6 +209,7 @@
         "client_side_commands": [
             "AdapterCreateDevice",
             "AdapterGetFormatCapabilities",
+            "AdapterGetInfo",
             "AdapterGetProperties",
             "AdapterGetLimits",
             "AdapterHasFeature",
diff --git a/src/dawn/native/Adapter.cpp b/src/dawn/native/Adapter.cpp
index 819fe20..442a52f 100644
--- a/src/dawn/native/Adapter.cpp
+++ b/src/dawn/native/Adapter.cpp
@@ -119,6 +119,42 @@
     return wgpu::Status::Success;
 }
 
+wgpu::Status AdapterBase::APIGetInfo(AdapterInfo* info) const {
+    DAWN_ASSERT(info != nullptr);
+
+    // Get lengths, with null terminators.
+    size_t vendorCLen = mPhysicalDevice->GetVendorName().length() + 1;
+    size_t architectureCLen = mPhysicalDevice->GetArchitectureName().length() + 1;
+    size_t deviceCLen = mPhysicalDevice->GetName().length() + 1;
+    size_t descriptionCLen = mPhysicalDevice->GetDriverDescription().length() + 1;
+
+    // Allocate space for all strings.
+    char* ptr = new char[vendorCLen + architectureCLen + deviceCLen + descriptionCLen];
+
+    info->vendor = ptr;
+    memcpy(ptr, mPhysicalDevice->GetVendorName().c_str(), vendorCLen);
+    ptr += vendorCLen;
+
+    info->architecture = ptr;
+    memcpy(ptr, mPhysicalDevice->GetArchitectureName().c_str(), architectureCLen);
+    ptr += architectureCLen;
+
+    info->device = ptr;
+    memcpy(ptr, mPhysicalDevice->GetName().c_str(), deviceCLen);
+    ptr += deviceCLen;
+
+    info->description = ptr;
+    memcpy(ptr, mPhysicalDevice->GetDriverDescription().c_str(), descriptionCLen);
+    ptr += descriptionCLen;
+
+    info->backendType = mPhysicalDevice->GetBackendType();
+    info->adapterType = mPhysicalDevice->GetAdapterType();
+    info->vendorID = mPhysicalDevice->GetVendorId();
+    info->deviceID = mPhysicalDevice->GetDeviceId();
+
+    return wgpu::Status::Success;
+}
+
 wgpu::Status AdapterBase::APIGetProperties(AdapterProperties* properties) const {
     DAWN_ASSERT(properties != nullptr);
     UnpackedPtr<AdapterProperties> unpacked;
@@ -186,6 +222,11 @@
     return wgpu::Status::Success;
 }
 
+void APIAdapterInfoFreeMembers(WGPUAdapterInfo info) {
+    // This single delete is enough because everything is a single allocation.
+    delete[] info.vendor;
+}
+
 void APIAdapterPropertiesFreeMembers(WGPUAdapterProperties properties) {
     // This single delete is enough because everything is a single allocation.
     delete[] properties.vendorName;
diff --git a/src/dawn/native/Adapter.h b/src/dawn/native/Adapter.h
index 0404c6a..a6dc732 100644
--- a/src/dawn/native/Adapter.h
+++ b/src/dawn/native/Adapter.h
@@ -61,6 +61,7 @@
     // WebGPU API
     InstanceBase* APIGetInstance() const;
     wgpu::Status APIGetLimits(SupportedLimits* limits) const;
+    wgpu::Status APIGetInfo(AdapterInfo* info) const;
     wgpu::Status APIGetProperties(AdapterProperties* properties) const;
     bool APIHasFeature(wgpu::FeatureName feature) const;
     size_t APIEnumerateFeatures(wgpu::FeatureName* features) const;
diff --git a/src/dawn/tests/end2end/AdapterCreationTests.cpp b/src/dawn/tests/end2end/AdapterCreationTests.cpp
index ac45137..3e0a486 100644
--- a/src/dawn/tests/end2end/AdapterCreationTests.cpp
+++ b/src/dawn/tests/end2end/AdapterCreationTests.cpp
@@ -180,6 +180,12 @@
 
         EXPECT_EQ(properties.adapterType, wgpu::AdapterType::CPU);
         EXPECT_TRUE(gpu_info::IsGoogleSwiftshader(properties.vendorID, properties.deviceID));
+
+        wgpu::AdapterInfo info;
+        adapter.GetInfo(&info);
+
+        EXPECT_EQ(info.adapterType, wgpu::AdapterType::CPU);
+        EXPECT_TRUE(gpu_info::IsGoogleSwiftshader(info.vendorID, info.deviceID));
     }
 }
 
@@ -209,6 +215,10 @@
         adapter.GetProperties(&properties);
         EXPECT_EQ(properties.adapterType, wgpu::AdapterType::DiscreteGPU);
         EXPECT_EQ(powerPreferenceProperties.powerPreference, options.powerPreference);
+
+        wgpu::AdapterInfo info;
+        adapter.GetInfo(&info);
+        EXPECT_EQ(info.adapterType, wgpu::AdapterType::DiscreteGPU);
     }
 }
 
@@ -238,6 +248,10 @@
         adapter.GetProperties(&properties);
         EXPECT_EQ(properties.adapterType, wgpu::AdapterType::IntegratedGPU);
         EXPECT_EQ(powerPreferenceProperties.powerPreference, options.powerPreference);
+
+        wgpu::AdapterInfo info;
+        adapter.GetInfo(&info);
+        EXPECT_EQ(info.adapterType, wgpu::AdapterType::IntegratedGPU);
     }
 }
 
@@ -472,5 +486,174 @@
     std::string driverDescription = properties.driverDescription;
 }
 
+// Test that calling AdapterGetInfo returns separate allocations for strings.
+// However, the string contents are equivalent.
+TEST_P(AdapterCreationTest, InfoUnique) {
+    wgpu::RequestAdapterOptions options = {};
+
+    MockCallback<WGPURequestAdapterCallback> cb;
+
+    WGPUAdapter cAdapter = nullptr;
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+        .WillOnce(SaveArg<1>(&cAdapter));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
+
+    wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
+    EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
+    if (!adapter) {
+        return;
+    }
+
+    wgpu::AdapterInfo info1;
+    wgpu::AdapterInfo info2;
+    adapter.GetInfo(&info1);
+    adapter.GetInfo(&info2);
+
+    EXPECT_NE(info1.vendor, info2.vendor);
+    EXPECT_STREQ(info1.vendor, info2.vendor);
+    EXPECT_NE(info1.architecture, info2.architecture);
+    EXPECT_STREQ(info1.architecture, info2.architecture);
+    EXPECT_NE(info1.device, info2.device);
+    EXPECT_STREQ(info1.device, info2.device);
+    EXPECT_NE(info1.description, info2.description);
+    EXPECT_STREQ(info1.description, info2.description);
+}
+
+// Test move assignment of the adapter info.
+TEST_P(AdapterCreationTest, InfoMoveAssign) {
+    wgpu::RequestAdapterOptions options = {};
+
+    MockCallback<WGPURequestAdapterCallback> cb;
+
+    WGPUAdapter cAdapter = nullptr;
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+        .WillOnce(SaveArg<1>(&cAdapter));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
+
+    wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
+    EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
+    if (!adapter) {
+        return;
+    }
+
+    wgpu::AdapterInfo info1;
+    wgpu::AdapterInfo info2;
+    adapter.GetInfo(&info1);
+    adapter.GetInfo(&info2);
+
+    std::string vendor = info1.vendor;
+    std::string architecture = info1.architecture;
+    std::string device = info1.device;
+    std::string description = info1.description;
+    wgpu::BackendType backendType = info1.backendType;
+    wgpu::AdapterType adapterType = info1.adapterType;
+    uint32_t vendorID = info1.vendorID;
+    uint32_t deviceID = info1.deviceID;
+
+    info2 = std::move(info1);
+
+    // Expect info2 to have info1's old contents.
+    EXPECT_STREQ(info2.vendor, vendor.c_str());
+    EXPECT_STREQ(info2.architecture, architecture.c_str());
+    EXPECT_STREQ(info2.device, device.c_str());
+    EXPECT_STREQ(info2.description, description.c_str());
+    EXPECT_EQ(info2.backendType, backendType);
+    EXPECT_EQ(info2.adapterType, adapterType);
+    EXPECT_EQ(info2.vendorID, vendorID);
+    EXPECT_EQ(info2.deviceID, deviceID);
+
+    // Expect info1 to be empty.
+    EXPECT_EQ(info1.vendor, nullptr);
+    EXPECT_EQ(info1.architecture, nullptr);
+    EXPECT_EQ(info1.device, nullptr);
+    EXPECT_EQ(info1.description, nullptr);
+    EXPECT_EQ(info1.backendType, static_cast<wgpu::BackendType>(0));
+    EXPECT_EQ(info1.adapterType, static_cast<wgpu::AdapterType>(0));
+    EXPECT_EQ(info1.vendorID, 0u);
+    EXPECT_EQ(info1.deviceID, 0u);
+}
+
+// Test move construction of the adapter info.
+TEST_P(AdapterCreationTest, InfoMoveConstruct) {
+    wgpu::RequestAdapterOptions options = {};
+
+    MockCallback<WGPURequestAdapterCallback> cb;
+
+    WGPUAdapter cAdapter = nullptr;
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+        .WillOnce(SaveArg<1>(&cAdapter));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
+
+    wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
+    EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
+    if (!adapter) {
+        return;
+    }
+
+    wgpu::AdapterInfo info1;
+    adapter.GetInfo(&info1);
+
+    std::string vendor = info1.vendor;
+    std::string architecture = info1.architecture;
+    std::string device = info1.device;
+    std::string description = info1.description;
+    wgpu::BackendType backendType = info1.backendType;
+    wgpu::AdapterType adapterType = info1.adapterType;
+    uint32_t vendorID = info1.vendorID;
+    uint32_t deviceID = info1.deviceID;
+
+    wgpu::AdapterInfo info2(std::move(info1));
+
+    // Expect info2 to have info1's old contents.
+    EXPECT_STREQ(info2.vendor, vendor.c_str());
+    EXPECT_STREQ(info2.architecture, architecture.c_str());
+    EXPECT_STREQ(info2.device, device.c_str());
+    EXPECT_STREQ(info2.description, description.c_str());
+    EXPECT_EQ(info2.backendType, backendType);
+    EXPECT_EQ(info2.adapterType, adapterType);
+    EXPECT_EQ(info2.vendorID, vendorID);
+    EXPECT_EQ(info2.deviceID, deviceID);
+
+    // Expect info1 to be empty.
+    EXPECT_EQ(info1.vendor, nullptr);
+    EXPECT_EQ(info1.architecture, nullptr);
+    EXPECT_EQ(info1.device, nullptr);
+    EXPECT_EQ(info1.description, nullptr);
+    EXPECT_EQ(info1.backendType, static_cast<wgpu::BackendType>(0));
+    EXPECT_EQ(info1.adapterType, static_cast<wgpu::AdapterType>(0));
+    EXPECT_EQ(info1.vendorID, 0u);
+    EXPECT_EQ(info1.deviceID, 0u);
+}
+
+// Test that the adapter info can outlive the adapter.
+TEST_P(AdapterCreationTest, InfoOutliveAdapter) {
+    wgpu::RequestAdapterOptions options = {};
+
+    MockCallback<WGPURequestAdapterCallback> cb;
+
+    WGPUAdapter cAdapter = nullptr;
+    EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, _, nullptr, this))
+        .WillOnce(SaveArg<1>(&cAdapter));
+    RequestAdapter(instance, &options, cb.Callback(), cb.MakeUserdata(this));
+
+    wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
+    EXPECT_EQ(adapter != nullptr, anyAdapterAvailable);
+    if (!adapter) {
+        return;
+    }
+
+    wgpu::AdapterInfo info;
+    adapter.GetInfo(&info);
+
+    // Copy the info to std::string, this should not be a use-after-free.
+    std::string vendor = info.vendor;
+    std::string architecture = info.architecture;
+    std::string device = info.device;
+    std::string description = info.description;
+
+    // Release the adapter.
+    adapter = nullptr;
+}
+
 }  // anonymous namespace
 }  // namespace dawn
diff --git a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
index f92488f..0a97e51 100644
--- a/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireInstanceTests.cpp
@@ -118,6 +118,16 @@
     WGPURequestAdapterOptions options = {};
     InstanceRequestAdapter(instance, &options, nullptr);
 
+    WGPUAdapterInfo fakeInfo = {};
+    fakeInfo.vendor = "fake-vendor";
+    fakeInfo.architecture = "fake-architecture";
+    fakeInfo.device = "fake adapter";
+    fakeInfo.description = "hello world";
+    fakeInfo.backendType = WGPUBackendType_D3D12;
+    fakeInfo.adapterType = WGPUAdapterType_IntegratedGPU;
+    fakeInfo.vendorID = 0x134;
+    fakeInfo.deviceID = 0x918;
+
     WGPUAdapterProperties fakeProperties = {};
     fakeProperties.vendorID = 0x134;
     fakeProperties.vendorName = "fake-vendor";
@@ -144,6 +154,12 @@
         .WillOnce(InvokeWithoutArgs([&] {
             EXPECT_CALL(api, AdapterHasFeature(apiAdapter, _)).WillRepeatedly(Return(false));
 
+            EXPECT_CALL(api, AdapterGetInfo(apiAdapter, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterInfo* info) {
+                    *info = fakeInfo;
+                    return WGPUStatus_Success;
+                })));
+
             EXPECT_CALL(api, AdapterGetProperties(apiAdapter, NotNull()))
                 .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterProperties* properties) {
                     *properties = fakeProperties;
@@ -177,6 +193,17 @@
     ExpectWireCallbacksWhen([&](auto& mockCb) {
         EXPECT_CALL(mockCb, Call(WGPURequestAdapterStatus_Success, NotNull(), nullptr, nullptr))
             .WillOnce(WithArg<1>(Invoke([&](WGPUAdapter adapter) {
+                WGPUAdapterInfo info = {};
+                wgpuAdapterGetInfo(adapter, &info);
+                EXPECT_STREQ(info.vendor, fakeInfo.vendor);
+                EXPECT_STREQ(info.architecture, fakeInfo.architecture);
+                EXPECT_STREQ(info.device, fakeInfo.device);
+                EXPECT_STREQ(info.description, fakeInfo.description);
+                EXPECT_EQ(info.backendType, fakeInfo.backendType);
+                EXPECT_EQ(info.adapterType, fakeInfo.adapterType);
+                EXPECT_EQ(info.vendorID, fakeInfo.vendorID);
+                EXPECT_EQ(info.deviceID, fakeInfo.deviceID);
+
                 WGPUAdapterProperties properties = {};
                 wgpuAdapterGetProperties(adapter, &properties);
                 EXPECT_EQ(properties.vendorID, fakeProperties.vendorID);
@@ -247,6 +274,15 @@
                 EXPECT_CALL(api, AdapterHasFeature(apiAdapter, feature)).WillOnce(Return(true));
             }
 
+            EXPECT_CALL(api, AdapterGetInfo(apiAdapter, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterInfo* info) {
+                    info->vendor = "fake-vendor";
+                    info->architecture = "fake-architecture";
+                    info->device = "fake adapter";
+                    info->description = "hello world";
+                    return WGPUStatus_Success;
+                })));
+
             EXPECT_CALL(api, AdapterGetProperties(apiAdapter, NotNull()))
                 .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterProperties* properties) {
                     properties->vendorName = "fake-vendor";
@@ -370,6 +406,15 @@
         .WillOnce(InvokeWithoutArgs([&] {
             EXPECT_CALL(api, AdapterHasFeature(apiAdapter, _)).WillRepeatedly(Return(false));
 
+            EXPECT_CALL(api, AdapterGetInfo(apiAdapter, NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterInfo* info) {
+                    info->vendor = "fake-vendor";
+                    info->architecture = "fake-architecture";
+                    info->device = "fake adapter";
+                    info->description = "hello world";
+                    return WGPUStatus_Success;
+                })));
+
             EXPECT_CALL(api, AdapterGetProperties(apiAdapter, NotNull()))
                 .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterProperties* properties) {
                     *properties = {};
diff --git a/src/dawn/tests/unittests/wire/WireTest.cpp b/src/dawn/tests/unittests/wire/WireTest.cpp
index 2cf9061..8fe6855 100644
--- a/src/dawn/tests/unittests/wire/WireTest.cpp
+++ b/src/dawn/tests/unittests/wire/WireTest.cpp
@@ -98,6 +98,16 @@
     EXPECT_CALL(api, OnInstanceRequestAdapter2(apiInstance, NotNull(), _)).WillOnce([&]() {
         EXPECT_CALL(api, AdapterHasFeature(apiAdapter, _)).WillRepeatedly(Return(false));
 
+        EXPECT_CALL(api, AdapterGetInfo(apiAdapter, NotNull()))
+            .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterInfo* info) {
+                *info = {};
+                info->vendor = "";
+                info->architecture = "";
+                info->device = "";
+                info->description = "";
+                return WGPUStatus_Success;
+            })));
+
         EXPECT_CALL(api, AdapterGetProperties(apiAdapter, NotNull()))
             .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterProperties* properties) {
                 *properties = {};
diff --git a/src/dawn/wire/client/Adapter.cpp b/src/dawn/wire/client/Adapter.cpp
index 0da876c..4b90183 100644
--- a/src/dawn/wire/client/Adapter.cpp
+++ b/src/dawn/wire/client/Adapter.cpp
@@ -157,6 +157,10 @@
     return mLimitsAndFeatures.SetFeatures(features, featuresCount);
 }
 
+void Adapter::SetInfo(const WGPUAdapterInfo* info) {
+    mInfo = *info;
+}
+
 void Adapter::SetProperties(const WGPUAdapterProperties* properties) {
     mProperties = *properties;
     mProperties.nextInChain = nullptr;
@@ -192,6 +196,37 @@
     }
 }
 
+WGPUStatus Adapter::GetInfo(WGPUAdapterInfo* info) const {
+    *info = mInfo;
+
+    // Get lengths, with null terminators.
+    size_t vendorCLen = strlen(mInfo.vendor) + 1;
+    size_t architectureCLen = strlen(mInfo.architecture) + 1;
+    size_t deviceCLen = strlen(mInfo.device) + 1;
+    size_t descriptionCLen = strlen(mInfo.description) + 1;
+
+    // Allocate space for all strings.
+    char* ptr = new char[vendorCLen + architectureCLen + deviceCLen + descriptionCLen];
+
+    info->vendor = ptr;
+    memcpy(ptr, mInfo.vendor, vendorCLen);
+    ptr += vendorCLen;
+
+    info->architecture = ptr;
+    memcpy(ptr, mInfo.architecture, architectureCLen);
+    ptr += architectureCLen;
+
+    info->device = ptr;
+    memcpy(ptr, mInfo.device, deviceCLen);
+    ptr += deviceCLen;
+
+    info->description = ptr;
+    memcpy(ptr, mInfo.description, descriptionCLen);
+    ptr += descriptionCLen;
+
+    return WGPUStatus_Success;
+}
+
 WGPUStatus Adapter::GetProperties(WGPUAdapterProperties* properties) const {
     // Loop through the chained struct.
     WGPUChainedStructOut* chain = properties->nextInChain;
@@ -367,6 +402,11 @@
 
 }  // namespace dawn::wire::client
 
+DAWN_WIRE_EXPORT void wgpuDawnWireClientAdapterInfoFreeMembers(WGPUAdapterInfo info) {
+    // This single delete is enough because everything is a single allocation.
+    delete[] info.vendor;
+}
+
 DAWN_WIRE_EXPORT void wgpuDawnWireClientAdapterPropertiesFreeMembers(
     WGPUAdapterProperties properties) {
     // This single delete is enough because everything is a single allocation.
diff --git a/src/dawn/wire/client/Adapter.h b/src/dawn/wire/client/Adapter.h
index bb23d0e..7e9fa6f 100644
--- a/src/dawn/wire/client/Adapter.h
+++ b/src/dawn/wire/client/Adapter.h
@@ -49,7 +49,9 @@
     size_t EnumerateFeatures(WGPUFeatureName* features) const;
     void SetLimits(const WGPUSupportedLimits* limits);
     void SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount);
+    void SetInfo(const WGPUAdapterInfo* info);
     void SetProperties(const WGPUAdapterProperties* properties);
+    WGPUStatus GetInfo(WGPUAdapterInfo* info) const;
     WGPUStatus GetProperties(WGPUAdapterProperties* properties) const;
     void RequestDevice(const WGPUDeviceDescriptor* descriptor,
                        WGPURequestDeviceCallback callback,
@@ -68,6 +70,7 @@
   private:
     LimitsAndFeatures mLimitsAndFeatures;
     WGPUAdapterProperties mProperties;
+    WGPUAdapterInfo mInfo;
     std::vector<WGPUMemoryHeapInfo> mMemoryHeapInfo;
     WGPUAdapterPropertiesD3D mD3DProperties;
     WGPUAdapterPropertiesVk mVkProperties;
diff --git a/src/dawn/wire/client/Instance.cpp b/src/dawn/wire/client/Instance.cpp
index acfdc8c..82cd558 100644
--- a/src/dawn/wire/client/Instance.cpp
+++ b/src/dawn/wire/client/Instance.cpp
@@ -66,6 +66,7 @@
     WireResult ReadyHook(FutureID futureID,
                          WGPURequestAdapterStatus status,
                          const char* message,
+                         const WGPUAdapterInfo* info,
                          const WGPUAdapterProperties* properties,
                          const WGPUSupportedLimits* limits,
                          uint32_t featuresCount,
@@ -76,6 +77,7 @@
             mMessage = message;
         }
         if (status == WGPURequestAdapterStatus_Success) {
+            mAdapter->SetInfo(info);
             mAdapter->SetProperties(properties);
             mAdapter->SetLimits(limits);
             mAdapter->SetFeatures(features, featuresCount);
@@ -248,12 +250,13 @@
                                                     WGPUFuture future,
                                                     WGPURequestAdapterStatus status,
                                                     const char* message,
+                                                    const WGPUAdapterInfo* info,
                                                     const WGPUAdapterProperties* properties,
                                                     const WGPUSupportedLimits* limits,
                                                     uint32_t featuresCount,
                                                     const WGPUFeatureName* features) {
     return GetEventManager(eventManager)
-        .SetFutureReady<RequestAdapterEvent>(future.id, status, message, properties, limits,
+        .SetFutureReady<RequestAdapterEvent>(future.id, status, message, info, properties, limits,
                                              featuresCount, features);
 }
 
diff --git a/src/dawn/wire/server/ServerInstance.cpp b/src/dawn/wire/server/ServerInstance.cpp
index a204691..200df16 100644
--- a/src/dawn/wire/server/ServerInstance.cpp
+++ b/src/dawn/wire/server/ServerInstance.cpp
@@ -98,6 +98,10 @@
     cmd.featuresCount = std::distance(features.begin(), it);
     cmd.features = features.data();
 
+    WGPUAdapterInfo info = {};
+    mProcs.adapterGetInfo(adapter, &info);
+    cmd.info = &info;
+
     // Query and report the adapter properties.
     WGPUAdapterProperties properties = {};
     WGPUChainedStructOut** propertiesChain = &properties.nextInChain;
@@ -140,6 +144,7 @@
     cmd.limits = &limits;
 
     SerializeCommand(cmd);
+    mProcs.adapterInfoFreeMembers(info);
     mProcs.adapterPropertiesFreeMembers(properties);
     mProcs.adapterPropertiesMemoryHeapsFreeMembers(memoryHeapProperties);
 }
diff --git a/tools/android/BUILD.gn b/tools/android/BUILD.gn
index 49fc29f..6b57f23 100644
--- a/tools/android/BUILD.gn
+++ b/tools/android/BUILD.gn
@@ -30,6 +30,7 @@
 dawn_json_generator("kotlin_gen") {
   target = "kotlin"
   outputs = [
+    "java/android/dawn/AdapterInfo.kt",
     "java/android/dawn/AdapterProperties.kt",
     "java/android/dawn/AdapterType.kt",
     "java/android/dawn/AddressMode.kt",