1 /*
2  * Copyright (C) 2018 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 "libdm/dm.h"
18 
19 #include <linux/dm-ioctl.h>
20 #include <sys/ioctl.h>
21 #include <sys/sysmacros.h>
22 #include <sys/types.h>
23 
24 #include <chrono>
25 #include <functional>
26 #include <string_view>
27 #include <thread>
28 
29 #include <android-base/file.h>
30 #include <android-base/logging.h>
31 #include <android-base/macros.h>
32 #include <android-base/properties.h>
33 #include <android-base/strings.h>
34 #include <uuid/uuid.h>
35 
36 #include "utility.h"
37 
38 namespace android {
39 namespace dm {
40 
41 using namespace std::literals;
42 
DeviceMapper()43 DeviceMapper::DeviceMapper() : fd_(-1) {
44     fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
45     if (fd_ < 0) {
46         PLOG(ERROR) << "Failed to open device-mapper";
47     }
48 }
49 
Instance()50 DeviceMapper& DeviceMapper::Instance() {
51     static DeviceMapper instance;
52     return instance;
53 }
54 
55 // Creates a new device mapper device
CreateDevice(const std::string & name,const std::string & uuid)56 bool DeviceMapper::CreateDevice(const std::string& name, const std::string& uuid) {
57     if (name.empty()) {
58         LOG(ERROR) << "Unnamed device mapper device creation is not supported";
59         return false;
60     }
61     if (name.size() >= DM_NAME_LEN) {
62         LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
63         return false;
64     }
65 
66     struct dm_ioctl io;
67     InitIo(&io, name);
68     if (!uuid.empty()) {
69         snprintf(io.uuid, sizeof(io.uuid), "%s", uuid.c_str());
70     }
71 
72     if (ioctl(fd_, DM_DEV_CREATE, &io)) {
73         PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
74         return false;
75     }
76 
77     // Check to make sure the newly created device doesn't already have targets
78     // added or opened by someone
79     CHECK(io.target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
80     CHECK(io.open_count == 0) << "Unexpected opens for newly created [" << name << "] device";
81 
82     // Creates a new device mapper device with the name passed in
83     return true;
84 }
85 
DeleteDeviceIfExists(const std::string & name,const std::chrono::milliseconds & timeout_ms)86 bool DeviceMapper::DeleteDeviceIfExists(const std::string& name,
87                                         const std::chrono::milliseconds& timeout_ms) {
88     if (GetState(name) == DmDeviceState::INVALID) {
89         return true;
90     }
91     return DeleteDevice(name, timeout_ms);
92 }
93 
DeleteDeviceIfExists(const std::string & name)94 bool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {
95     return DeleteDeviceIfExists(name, 0ms);
96 }
97 
DeleteDevice(const std::string & name,const std::chrono::milliseconds & timeout_ms)98 bool DeviceMapper::DeleteDevice(const std::string& name,
99                                 const std::chrono::milliseconds& timeout_ms) {
100     std::string unique_path;
101     if (!GetDeviceUniquePath(name, &unique_path)) {
102         LOG(ERROR) << "Failed to get unique path for device " << name;
103     }
104     struct dm_ioctl io;
105     InitIo(&io, name);
106 
107     if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
108         PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << name << "]";
109         return false;
110     }
111 
112     // Check to make sure appropriate uevent is generated so ueventd will
113     // do the right thing and remove the corresponding device node and symlinks.
114     CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
115             << "Didn't generate uevent for [" << name << "] removal";
116 
117     if (timeout_ms <= std::chrono::milliseconds::zero()) {
118         return true;
119     }
120     if (unique_path.empty()) {
121         return false;
122     }
123     if (!WaitForFileDeleted(unique_path, timeout_ms)) {
124         LOG(ERROR) << "Failed waiting for " << unique_path << " to be deleted";
125         return false;
126     }
127     return true;
128 }
129 
DeleteDevice(const std::string & name)130 bool DeviceMapper::DeleteDevice(const std::string& name) {
131     return DeleteDevice(name, 0ms);
132 }
133 
GenerateUuid()134 static std::string GenerateUuid() {
135     uuid_t uuid_bytes;
136     uuid_generate(uuid_bytes);
137 
138     char uuid_chars[37] = {};
139     uuid_unparse_lower(uuid_bytes, uuid_chars);
140 
141     return std::string{uuid_chars};
142 }
143 
IsRecovery()144 static bool IsRecovery() {
145     return access("/system/bin/recovery", F_OK) == 0;
146 }
147 
CreateDevice(const std::string & name,const DmTable & table,std::string * path,const std::chrono::milliseconds & timeout_ms)148 bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
149                                 const std::chrono::milliseconds& timeout_ms) {
150     std::string uuid = GenerateUuid();
151     if (!CreateDevice(name, uuid)) {
152         return false;
153     }
154 
155     // We use the unique path for testing whether the device is ready. After
156     // that, it's safe to use the dm-N path which is compatible with callers
157     // that expect it to be formatted as such.
158     std::string unique_path;
159     if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
160         !GetDmDevicePathByName(name, path)) {
161         DeleteDevice(name);
162         return false;
163     }
164 
165     if (timeout_ms <= std::chrono::milliseconds::zero()) {
166         return true;
167     }
168 
169     if (IsRecovery()) {
170         bool non_ab_device = android::base::GetProperty("ro.build.ab_update", "").empty();
171         int sdk = android::base::GetIntProperty("ro.build.version.sdk", 0);
172         if (non_ab_device && sdk && sdk <= 29) {
173             LOG(INFO) << "Detected ueventd incompatibility, reverting to legacy libdm behavior.";
174             unique_path = *path;
175         }
176     }
177 
178     if (!WaitForFile(unique_path, timeout_ms)) {
179         LOG(ERROR) << "Failed waiting for device path: " << unique_path;
180         DeleteDevice(name);
181         return false;
182     }
183     return true;
184 }
185 
GetDeviceUniquePath(const std::string & name,std::string * path)186 bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
187     struct dm_ioctl io;
188     InitIo(&io, name);
189     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
190         PLOG(ERROR) << "Failed to get device path: " << name;
191         return false;
192     }
193 
194     if (io.uuid[0] == '\0') {
195         LOG(ERROR) << "Device does not have a unique path: " << name;
196         return false;
197     }
198     *path = "/dev/block/mapper/by-uuid/"s + io.uuid;
199     return true;
200 }
201 
GetDetailedInfo(const std::string & name) const202 std::optional<DeviceMapper::Info> DeviceMapper::GetDetailedInfo(const std::string& name) const {
203     struct dm_ioctl io;
204     InitIo(&io, name);
205     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
206         return std::nullopt;
207     }
208     return Info(io.flags);
209 }
210 
GetState(const std::string & name) const211 DmDeviceState DeviceMapper::GetState(const std::string& name) const {
212     struct dm_ioctl io;
213     InitIo(&io, name);
214     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
215         return DmDeviceState::INVALID;
216     }
217     if ((io.flags & DM_ACTIVE_PRESENT_FLAG) && !(io.flags & DM_SUSPEND_FLAG)) {
218         return DmDeviceState::ACTIVE;
219     }
220     return DmDeviceState::SUSPENDED;
221 }
222 
ChangeState(const std::string & name,DmDeviceState state)223 bool DeviceMapper::ChangeState(const std::string& name, DmDeviceState state) {
224     if (state != DmDeviceState::SUSPENDED && state != DmDeviceState::ACTIVE) {
225         return false;
226     }
227 
228     struct dm_ioctl io;
229     InitIo(&io, name);
230 
231     if (state == DmDeviceState::SUSPENDED) io.flags = DM_SUSPEND_FLAG;
232 
233     if (ioctl(fd_, DM_DEV_SUSPEND, &io) < 0) {
234         PLOG(ERROR) << "DM_DEV_SUSPEND "
235                     << (state == DmDeviceState::SUSPENDED ? "suspend" : "resume") << " failed";
236         return false;
237     }
238     return true;
239 }
240 
CreateDevice(const std::string & name,const DmTable & table)241 bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
242     std::string ignore_path;
243     if (!CreateDevice(name, table, &ignore_path, 0ms)) {
244         return false;
245     }
246     return true;
247 }
248 
LoadTableAndActivate(const std::string & name,const DmTable & table)249 bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
250     std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
251     ioctl_buffer += table.Serialize();
252 
253     struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
254     InitIo(io, name);
255     io->data_size = ioctl_buffer.size();
256     io->data_start = sizeof(struct dm_ioctl);
257     io->target_count = static_cast<uint32_t>(table.num_targets());
258     if (table.readonly()) {
259         io->flags |= DM_READONLY_FLAG;
260     }
261     if (ioctl(fd_, DM_TABLE_LOAD, io)) {
262         PLOG(ERROR) << "DM_TABLE_LOAD failed";
263         return false;
264     }
265 
266     InitIo(io, name);
267     if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
268         PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
269         return false;
270     }
271     return true;
272 }
273 
274 // Reads all the available device mapper targets and their corresponding
275 // versions from the kernel and returns in a vector
GetAvailableTargets(std::vector<DmTargetTypeInfo> * targets)276 bool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets) {
277     targets->clear();
278 
279     // calculate the space needed to read a maximum of kMaxPossibleDmTargets
280     uint32_t payload_size = sizeof(struct dm_target_versions);
281     payload_size += DM_MAX_TYPE_NAME;
282     // device mapper wants every target spec to be aligned at 8-byte boundary
283     payload_size = DM_ALIGN(payload_size);
284     payload_size *= kMaxPossibleDmTargets;
285 
286     uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
287     auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
288     if (buffer == nullptr) {
289         LOG(ERROR) << "failed to allocate memory";
290         return false;
291     }
292 
293     // Sets appropriate data size and data_start to make sure we tell kernel
294     // about the total size of the buffer we are passing and where to start
295     // writing the list of targets.
296     struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
297     InitIo(io);
298     io->data_size = data_size;
299     io->data_start = sizeof(*io);
300 
301     if (ioctl(fd_, DM_LIST_VERSIONS, io)) {
302         PLOG(ERROR) << "DM_LIST_VERSIONS failed";
303         return false;
304     }
305 
306     // If the provided buffer wasn't enough to list all targets, note that
307     // any data beyond sizeof(*io) must not be read in this case
308     if (io->flags & DM_BUFFER_FULL_FLAG) {
309         LOG(INFO) << data_size << " is not enough memory to list all dm targets";
310         return false;
311     }
312 
313     // if there are no targets registered, return success with empty vector
314     if (io->data_size == sizeof(*io)) {
315         return true;
316     }
317 
318     // Parse each target and list the name and version
319     // TODO(b/110035986): Templatize this
320     uint32_t next = sizeof(*io);
321     data_size = io->data_size - next;
322     struct dm_target_versions* vers =
323             reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);
324     while (next && data_size) {
325         targets->emplace_back(vers);
326         if (vers->next == 0) {
327             break;
328         }
329         next += vers->next;
330         data_size -= vers->next;
331         vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) +
332                                                             next);
333     }
334 
335     return true;
336 }
337 
GetTargetByName(const std::string & name,DmTargetTypeInfo * info)338 bool DeviceMapper::GetTargetByName(const std::string& name, DmTargetTypeInfo* info) {
339     std::vector<DmTargetTypeInfo> targets;
340     if (!GetAvailableTargets(&targets)) {
341         return false;
342     }
343     for (const auto& target : targets) {
344         if (target.name() == name) {
345             if (info) *info = target;
346             return true;
347         }
348     }
349     return false;
350 }
351 
GetAvailableDevices(std::vector<DmBlockDevice> * devices)352 bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
353     devices->clear();
354 
355     // calculate the space needed to read a maximum of 256 targets, each with
356     // name with maximum length of 16 bytes
357     uint32_t payload_size = sizeof(struct dm_name_list);
358     // 128-bytes for the name
359     payload_size += DM_NAME_LEN;
360     // dm wants every device spec to be aligned at 8-byte boundary
361     payload_size = DM_ALIGN(payload_size);
362     payload_size *= kMaxPossibleDmDevices;
363     uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
364     auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
365     if (buffer == nullptr) {
366         LOG(ERROR) << "failed to allocate memory";
367         return false;
368     }
369 
370     // Sets appropriate data size and data_start to make sure we tell kernel
371     // about the total size of the buffer we are passing and where to start
372     // writing the list of targets.
373     struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
374     InitIo(io);
375     io->data_size = data_size;
376     io->data_start = sizeof(*io);
377 
378     if (ioctl(fd_, DM_LIST_DEVICES, io)) {
379         PLOG(ERROR) << "DM_LIST_DEVICES failed";
380         return false;
381     }
382 
383     // If the provided buffer wasn't enough to list all devices any data
384     // beyond sizeof(*io) must not be read.
385     if (io->flags & DM_BUFFER_FULL_FLAG) {
386         LOG(INFO) << data_size << " is not enough memory to list all dm devices";
387         return false;
388     }
389 
390     // if there are no devices created yet, return success with empty vector
391     if (io->data_size == sizeof(*io)) {
392         return true;
393     }
394 
395     // Parse each device and add a new DmBlockDevice to the vector
396     // created from the kernel data.
397     uint32_t next = sizeof(*io);
398     data_size = io->data_size - next;
399     struct dm_name_list* dm_dev =
400             reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
401 
402     while (next && data_size) {
403         devices->emplace_back((dm_dev));
404         if (dm_dev->next == 0) {
405             break;
406         }
407         next += dm_dev->next;
408         data_size -= dm_dev->next;
409         dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
410     }
411 
412     return true;
413 }
414 
415 // Accepts a device mapper device name (like system_a, vendor_b etc) and
416 // returns the path to it's device node (or symlink to the device node)
GetDmDevicePathByName(const std::string & name,std::string * path)417 bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
418     struct dm_ioctl io;
419     InitIo(&io, name);
420     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
421         PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
422         return false;
423     }
424 
425     uint32_t dev_num = minor(io.dev);
426     *path = "/dev/block/dm-" + std::to_string(dev_num);
427     return true;
428 }
429 
GetDeviceNumber(const std::string & name,dev_t * dev)430 bool DeviceMapper::GetDeviceNumber(const std::string& name, dev_t* dev) {
431     struct dm_ioctl io;
432     InitIo(&io, name);
433     if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
434         PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
435         return false;
436     }
437     *dev = io.dev;
438     return true;
439 }
440 
GetDeviceString(const std::string & name,std::string * dev)441 bool DeviceMapper::GetDeviceString(const std::string& name, std::string* dev) {
442     dev_t num;
443     if (!GetDeviceNumber(name, &num)) {
444         return false;
445     }
446     *dev = std::to_string(major(num)) + ":" + std::to_string(minor(num));
447     return true;
448 }
449 
GetTableStatus(const std::string & name,std::vector<TargetInfo> * table)450 bool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
451     return GetTable(name, 0, table);
452 }
453 
GetTableInfo(const std::string & name,std::vector<TargetInfo> * table)454 bool DeviceMapper::GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
455     return GetTable(name, DM_STATUS_TABLE_FLAG, table);
456 }
457 
458 // private methods of DeviceMapper
GetTable(const std::string & name,uint32_t flags,std::vector<TargetInfo> * table)459 bool DeviceMapper::GetTable(const std::string& name, uint32_t flags,
460                             std::vector<TargetInfo>* table) {
461     std::vector<char> buffer;
462     struct dm_ioctl* io = nullptr;
463 
464     for (buffer.resize(4096);; buffer.resize(buffer.size() * 2)) {
465         io = reinterpret_cast<struct dm_ioctl*>(&buffer[0]);
466 
467         InitIo(io, name);
468         io->data_size = buffer.size();
469         io->data_start = sizeof(*io);
470         io->flags = flags;
471         if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {
472             PLOG(ERROR) << "DM_TABLE_STATUS failed for " << name;
473             return false;
474         }
475         if (!(io->flags & DM_BUFFER_FULL_FLAG)) break;
476     }
477 
478     uint32_t cursor = io->data_start;
479     uint32_t data_end = std::min(io->data_size, uint32_t(buffer.size()));
480     for (uint32_t i = 0; i < io->target_count; i++) {
481         if (cursor + sizeof(struct dm_target_spec) > data_end) {
482             break;
483         }
484         // After each dm_target_spec is a status string. spec->next is an
485         // offset from |io->data_start|, and we clamp it to the size of our
486         // buffer.
487         struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&buffer[cursor]);
488         uint32_t data_offset = cursor + sizeof(dm_target_spec);
489         uint32_t next_cursor = std::min(io->data_start + spec->next, data_end);
490 
491         std::string data;
492         if (next_cursor > data_offset) {
493             // Note: we use c_str() to eliminate any extra trailing 0s.
494             data = std::string(&buffer[data_offset], next_cursor - data_offset).c_str();
495         }
496         table->emplace_back(*spec, data);
497         cursor = next_cursor;
498     }
499     return true;
500 }
501 
InitIo(struct dm_ioctl * io,const std::string & name) const502 void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
503     CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
504     memset(io, 0, sizeof(*io));
505 
506     io->version[0] = DM_VERSION0;
507     io->version[1] = DM_VERSION1;
508     io->version[2] = DM_VERSION2;
509     io->data_size = sizeof(*io);
510     io->data_start = 0;
511     if (!name.empty()) {
512         snprintf(io->name, sizeof(io->name), "%s", name.c_str());
513     }
514 }
515 
GetTargetType(const struct dm_target_spec & spec)516 std::string DeviceMapper::GetTargetType(const struct dm_target_spec& spec) {
517     if (const void* p = memchr(spec.target_type, '\0', sizeof(spec.target_type))) {
518         ptrdiff_t length = reinterpret_cast<const char*>(p) - spec.target_type;
519         return std::string{spec.target_type, static_cast<size_t>(length)};
520     }
521     return std::string{spec.target_type, sizeof(spec.target_type)};
522 }
523 
ExtractBlockDeviceName(const std::string & path,std::string * name)524 static bool ExtractBlockDeviceName(const std::string& path, std::string* name) {
525     static constexpr std::string_view kDevBlockPrefix("/dev/block/");
526     if (android::base::StartsWith(path, kDevBlockPrefix)) {
527         *name = path.substr(kDevBlockPrefix.length());
528         return true;
529     }
530     return false;
531 }
532 
IsDmBlockDevice(const std::string & path)533 bool DeviceMapper::IsDmBlockDevice(const std::string& path) {
534     std::string name;
535     if (!ExtractBlockDeviceName(path, &name)) {
536         return false;
537     }
538     return android::base::StartsWith(name, "dm-");
539 }
540 
GetDmDeviceNameByPath(const std::string & path)541 std::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) {
542     std::string name;
543     if (!ExtractBlockDeviceName(path, &name)) {
544         LOG(WARNING) << path << " is not a block device";
545         return std::nullopt;
546     }
547     if (!android::base::StartsWith(name, "dm-")) {
548         LOG(WARNING) << path << " is not a dm device";
549         return std::nullopt;
550     }
551     std::string dm_name_file = "/sys/block/" + name + "/dm/name";
552     std::string dm_name;
553     if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {
554         PLOG(ERROR) << "Failed to read file " << dm_name_file;
555         return std::nullopt;
556     }
557     dm_name = android::base::Trim(dm_name);
558     return dm_name;
559 }
560 
GetParentBlockDeviceByPath(const std::string & path)561 std::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) {
562     std::string name;
563     if (!ExtractBlockDeviceName(path, &name)) {
564         LOG(WARNING) << path << " is not a block device";
565         return std::nullopt;
566     }
567     if (!android::base::StartsWith(name, "dm-")) {
568         // Reached bottom of the device mapper stack.
569         return std::nullopt;
570     }
571     auto slaves_dir = "/sys/block/" + name + "/slaves";
572     auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir);
573     if (dir == nullptr) {
574         PLOG(ERROR) << "Failed to open: " << slaves_dir;
575         return std::nullopt;
576     }
577     std::string sub_device_name = "";
578     for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {
579         if (entry->d_type != DT_LNK) continue;
580         if (!sub_device_name.empty()) {
581             LOG(ERROR) << "Too many slaves in " << slaves_dir;
582             return std::nullopt;
583         }
584         sub_device_name = entry->d_name;
585     }
586     if (sub_device_name.empty()) {
587         LOG(ERROR) << "No slaves in " << slaves_dir;
588         return std::nullopt;
589     }
590     return "/dev/block/" + sub_device_name;
591 }
592 
IsOverflowSnapshot() const593 bool DeviceMapper::TargetInfo::IsOverflowSnapshot() const {
594     return spec.target_type == "snapshot"s && data == "Overflow"s;
595 }
596 
597 }  // namespace dm
598 }  // namespace android
599