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 // Versions of hwcomposer we implement:
18 // JB: 0.3
19 // JB-MR1 to N : 1.1
20 // N-MR1 to ... : We report 1.1 but SurfaceFlinger has the option to use an
21 // adapter to treat our 1.1 hwcomposer as a 2.0. If SF stops using that adapter
22 // to support 1.1 implementations it can be copied into cuttlefish from
23 // frameworks/native/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.*
24 
25 #define LOG_TAG "hwc.cf_x86"
26 #define HWC_REMOVE_DEPRECATED_VERSIONS 1
27 
28 #include "guest/hals/hwcomposer/common/hwcomposer.h"
29 
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <math.h>
33 #include <poll.h>
34 #include <pthread.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <sync/sync.h>
38 #include <sys/resource.h>
39 #include <sys/time.h>
40 
41 #include <sstream>
42 #include <string>
43 #include <vector>
44 
45 #include <cutils/compiler.h>
46 #include <cutils/properties.h>
47 #include <hardware/gralloc.h>
48 #include <hardware/hardware.h>
49 #include <hardware/hwcomposer.h>
50 #include <hardware/hwcomposer_defs.h>
51 #include <log/log.h>
52 #include <utils/String8.h>
53 #include <utils/Vector.h>
54 
55 #include "guest/hals/hwcomposer/common/base_composer.h"
56 #include "guest/hals/hwcomposer/common/cpu_composer.h"
57 #include "guest/hals/hwcomposer/common/geometry_utils.h"
58 #include "guest/hals/hwcomposer/common/hwcomposer.h"
59 
60 #ifdef USE_OLD_HWCOMPOSER
61 typedef cvd::BaseComposer ComposerType;
62 #else
63 typedef cvd::CpuComposer ComposerType;
64 #endif
65 
66 struct hwc_composer_device_data_t {
67   const hwc_procs_t* procs;
68   pthread_t vsync_thread;
69   int64_t vsync_base_timestamp;
70   int32_t vsync_period_ns;
71 };
72 
73 struct cvd_hwc_composer_device_1_t {
74   hwc_composer_device_1_t base;
75   hwc_composer_device_data_t vsync_data;
76   cvd::BaseComposer* composer;
77 };
78 
79 struct external_display_config_t {
80   uint64_t physicalId;
81   uint32_t width;
82   uint32_t height;
83   uint32_t dpi;
84   uint32_t flags;
85 };
86 
87 namespace {
88 
hwc_vsync_thread(void * data)89 void* hwc_vsync_thread(void* data) {
90   struct hwc_composer_device_data_t* pdev =
91       (struct hwc_composer_device_data_t*)data;
92   setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
93 
94   int64_t base_timestamp = pdev->vsync_base_timestamp;
95   int64_t last_logged = base_timestamp / 1e9;
96   int sent = 0;
97   int last_sent = 0;
98   static const int log_interval = 60;
99   void (*vsync_proc)(const struct hwc_procs*, int, int64_t) = nullptr;
100   bool log_no_procs = true, log_no_vsync = true;
101   while (true) {
102     struct timespec rt;
103     if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
104       LOG_ALWAYS_FATAL("%s:%d error in vsync thread clock_gettime: %s",
105                        __FILE__, __LINE__, strerror(errno));
106     }
107 
108     int64_t timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
109     // Given now's timestamp calculate the time of the next timestamp.
110     timestamp += pdev->vsync_period_ns -
111                  (timestamp - base_timestamp) % pdev->vsync_period_ns;
112 
113     rt.tv_sec = timestamp / 1e9;
114     rt.tv_nsec = timestamp % static_cast<int32_t>(1e9);
115     int err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &rt, NULL);
116     if (err == -1) {
117       ALOGE("error in vsync thread: %s", strerror(errno));
118       if (errno == EINTR) {
119         continue;
120       }
121     }
122 
123     // The vsync thread is started on device open, it may run before the
124     // registerProcs callback has a chance to be called, so we need to make sure
125     // procs is not NULL before dereferencing it.
126     if (pdev && pdev->procs) {
127       vsync_proc = pdev->procs->vsync;
128     } else if (log_no_procs) {
129       log_no_procs = false;
130       ALOGI("procs is not set yet, unable to deliver vsync event");
131     }
132     if (vsync_proc) {
133       vsync_proc(const_cast<hwc_procs_t*>(pdev->procs), 0, timestamp);
134       ++sent;
135     } else if (log_no_vsync) {
136       log_no_vsync = false;
137       ALOGE("vsync callback is null (but procs was already set)");
138     }
139     if (rt.tv_sec - last_logged > log_interval) {
140       ALOGI("Sent %d syncs in %ds", sent - last_sent, log_interval);
141       last_logged = rt.tv_sec;
142       last_sent = sent;
143     }
144   }
145 
146   return NULL;
147 }
148 
CompositionString(int type)149 std::string CompositionString(int type) {
150   switch (type) {
151     case HWC_FRAMEBUFFER:
152       return "Framebuffer";
153     case HWC_OVERLAY:
154       return "Overlay";
155     case HWC_BACKGROUND:
156       return "Background";
157     case HWC_FRAMEBUFFER_TARGET:
158       return "FramebufferTarget";
159     case HWC_SIDEBAND:
160       return "Sideband";
161     case HWC_CURSOR_OVERLAY:
162       return "CursorOverlay";
163     default:
164       return std::string("Unknown (") + std::to_string(type) + ")";
165   }
166 }
167 
LogLayers(int num_layers,hwc_layer_1_t * layers,int invalid)168 void LogLayers(int num_layers, hwc_layer_1_t* layers, int invalid) {
169   ALOGE("Layers:");
170   for (int idx = 0; idx < num_layers; ++idx) {
171     std::string log_line;
172     if (idx == invalid) {
173       log_line = "Invalid layer: ";
174     }
175     log_line +=
176         "Composition Type: " + CompositionString(layers[idx].compositionType);
177     ALOGE("%s", log_line.c_str());
178   }
179 }
180 
181 // Ensures that the layer does not include any inconsistencies
IsValidLayer(hwc_composer_device_1_t * dev,const hwc_layer_1_t & layer)182 bool IsValidLayer(hwc_composer_device_1_t* dev, const hwc_layer_1_t& layer) {
183   if (layer.flags & HWC_SKIP_LAYER) {
184     // A layer we are asked to skip validate should not be marked as skip
185     ALOGE("%s: Layer is marked as skip", __FUNCTION__);
186     return false;
187   }
188   // Check displayFrame
189   if (layer.displayFrame.left > layer.displayFrame.right ||
190       layer.displayFrame.top > layer.displayFrame.bottom) {
191     ALOGE(
192         "%s: Malformed rectangle (displayFrame): [left = %d, right = %d, top = "
193         "%d, bottom = %d]",
194         __FUNCTION__, layer.displayFrame.left, layer.displayFrame.right,
195         layer.displayFrame.top, layer.displayFrame.bottom);
196     return false;
197   }
198   // Check sourceCrop
199   if (layer.sourceCrop.left > layer.sourceCrop.right ||
200       layer.sourceCrop.top > layer.sourceCrop.bottom) {
201     ALOGE(
202         "%s: Malformed rectangle (sourceCrop): [left = %d, right = %d, top = "
203         "%d, bottom = %d]",
204         __FUNCTION__, layer.sourceCrop.left, layer.sourceCrop.right,
205         layer.sourceCrop.top, layer.sourceCrop.bottom);
206     return false;
207   }
208 
209   auto* cvd_hwc_dev = reinterpret_cast<cvd_hwc_composer_device_1_t*>(dev);
210   return cvd_hwc_dev->composer->IsValidLayer(layer);
211 }
212 
IsValidComposition(hwc_composer_device_1_t * dev,int num_layers,hwc_layer_1_t * layers,bool on_set)213 bool IsValidComposition(hwc_composer_device_1_t* dev, int num_layers,
214                         hwc_layer_1_t* layers, bool on_set) {
215   if (num_layers == 0) {
216     ALOGE("Composition requested with 0 layers");
217     return false;
218   }
219   // Sometimes the hwcomposer receives a prepare and set calls with no other
220   // layer than the FRAMEBUFFER_TARGET with a null handler. We treat this case
221   // independently as a valid composition, but issue a warning about it.
222   if (num_layers == 1 && layers[0].compositionType == HWC_FRAMEBUFFER_TARGET &&
223       layers[0].handle == NULL) {
224     ALOGW("Received request for empty composition, treating as valid noop");
225     return true;
226   }
227   // The FRAMEBUFFER_TARGET layer needs to be sane only if
228   // there is at least one layer marked HWC_FRAMEBUFFER or if there is no layer
229   // marked HWC_OVERLAY (i.e some layers where composed with OpenGL, no layer
230   // marked overlay or framebuffer means that surfaceflinger decided to go for
231   // OpenGL without asking the hwcomposer first)
232   bool check_fb_target = true;
233   for (int idx = 0; idx < num_layers; ++idx) {
234     if (layers[idx].compositionType == HWC_FRAMEBUFFER) {
235       // There is at least one, so it needs to be checked.
236       // It may have been set to false before, so ensure it's set to true.
237       check_fb_target = true;
238       break;
239     }
240     if (layers[idx].compositionType == HWC_OVERLAY) {
241       // At least one overlay, we may not need to.
242       check_fb_target = false;
243     }
244   }
245 
246   for (int idx = 0; idx < num_layers; ++idx) {
247     switch (layers[idx].compositionType) {
248       case HWC_FRAMEBUFFER_TARGET:
249         // In the call to prepare() the framebuffer target does not have a valid
250         // buffer_handle, so we don't validate it yet.
251         if (on_set && check_fb_target && !IsValidLayer(dev, layers[idx])) {
252           ALOGE("%s: Invalid layer found", __FUNCTION__);
253           LogLayers(num_layers, layers, idx);
254           return false;
255         }
256         break;
257       case HWC_OVERLAY:
258         if (!(layers[idx].flags & HWC_SKIP_LAYER) &&
259             !IsValidLayer(dev, layers[idx])) {
260           ALOGE("%s: Invalid layer found", __FUNCTION__);
261           LogLayers(num_layers, layers, idx);
262           return false;
263         }
264         break;
265     }
266   }
267   return true;
268 }
269 
270 // Note predefined "hwservicemanager." is used to avoid adding new selinux rules
271 #define EXTERANL_DISPLAY_PROP "hwservicemanager.external.displays"
272 
273 // return 0 for successful
274 // return < 0 if failed
GetExternalDisplayConfigs(std::vector<struct external_display_config_t> * configs)275 int GetExternalDisplayConfigs(std::vector<struct external_display_config_t>* configs) {
276   // this guest property, hwservicemanager.external.displays,
277   // specifies multi-display info, with comma (,) as separator
278   // each display has the following info:
279   //   physicalId,width,height,dpi,flags
280   // several displays can be provided, e.g., following has 2 displays:
281   // setprop hwservicemanager.external.displays 1,1200,800,120,0,2,1200,800,120,0
282   std::vector<uint64_t> values;
283   char displays_value[PROPERTY_VALUE_MAX] = "";
284   property_get(EXTERANL_DISPLAY_PROP, displays_value, "");
285   bool valid = displays_value[0] != '\0';
286   if (valid) {
287       char *p = displays_value;
288       while (*p) {
289           if (!isdigit(*p) && *p != ',' && *p != ' ') {
290               valid = false;
291               break;
292           }
293           p++;
294       }
295   }
296   if (!valid) {
297       // no external displays are specified
298       ALOGE("%s: Invalid syntax for the value of system prop: %s, value: %s",
299           __FUNCTION__, EXTERANL_DISPLAY_PROP, displays_value);
300       return 0;
301   }
302   // parse all int values to a vector
303   std::istringstream stream(displays_value);
304   for (uint64_t id; stream >> id;) {
305       values.push_back(id);
306       if (stream.peek() == ',')
307           stream.ignore();
308   }
309   // each display has 5 values
310   if ((values.size() % 5) != 0) {
311       ALOGE("%s: Invalid value for system property: %s", __FUNCTION__, EXTERANL_DISPLAY_PROP);
312       return -1;
313   }
314   while (!values.empty()) {
315       struct external_display_config_t config;
316       config.physicalId = values[0];
317       config.width = values[1];
318       config.height = values[2];
319       config.dpi = values[3];
320       config.flags = values[4];
321       values.erase(values.begin(), values.begin() + 5);
322       configs->push_back(config);
323   }
324   return 0;
325 }
326 
327 }  // namespace
328 
cvd_hwc_prepare(hwc_composer_device_1_t * dev,size_t numDisplays,hwc_display_contents_1_t ** displays)329 static int cvd_hwc_prepare(hwc_composer_device_1_t* dev, size_t numDisplays,
330                            hwc_display_contents_1_t** displays) {
331   if (!numDisplays || !displays) return 0;
332 
333   for (int disp = 0; disp < numDisplays; ++disp) {
334     hwc_display_contents_1_t* list = displays[disp];
335 
336     if (!list) return 0;
337     if (!IsValidComposition(dev, list->numHwLayers, &list->hwLayers[0], false)) {
338       LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
339       return -1;
340     }
341     reinterpret_cast<cvd_hwc_composer_device_1_t*>(dev)->composer->PrepareLayers(
342       list->numHwLayers, &list->hwLayers[0]);
343   }
344   return 0;
345 }
346 
cvd_hwc_set(hwc_composer_device_1_t * dev,size_t numDisplays,hwc_display_contents_1_t ** displays)347 static int cvd_hwc_set(hwc_composer_device_1_t* dev, size_t numDisplays,
348                        hwc_display_contents_1_t** displays) {
349   if (!numDisplays || !displays) return 0;
350 
351   int retval = -1;
352   for (int disp = 0; disp < numDisplays; ++disp) {
353     hwc_display_contents_1_t* contents = displays[disp];
354     if (!contents) return 0;
355 
356     hwc_layer_1_t* layers = &contents->hwLayers[0];
357     if (contents->numHwLayers == 1 &&
358       layers[0].compositionType == HWC_FRAMEBUFFER_TARGET) {
359       ALOGW("Received request for empty composition, treating as valid noop");
360       return 0;
361     }
362     if (!IsValidComposition(dev, contents->numHwLayers, layers, true)) {
363       LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
364       return -1;
365     }
366     retval =
367         reinterpret_cast<cvd_hwc_composer_device_1_t*>(dev)->composer->SetLayers(
368             contents->numHwLayers, layers);
369     if (retval != 0) break;
370 
371     int closedFds = 0;
372     for (size_t index = 0; index < contents->numHwLayers; ++index) {
373       if (layers[index].acquireFenceFd != -1) {
374         close(layers[index].acquireFenceFd);
375         layers[index].acquireFenceFd = -1;
376         ++closedFds;
377       }
378     }
379     if (closedFds) {
380       ALOGI("Saw %zu layers, closed=%d", contents->numHwLayers, closedFds);
381     }
382 
383     // TODO(ghartman): This should be set before returning. On the next set it
384     // should be signalled when we load the new frame.
385     contents->retireFenceFd = -1;
386   }
387 
388   return retval;
389 }
390 
cvd_hwc_register_procs(hwc_composer_device_1_t * dev,const hwc_procs_t * procs)391 static void cvd_hwc_register_procs(hwc_composer_device_1_t* dev,
392                                    const hwc_procs_t* procs) {
393   struct cvd_hwc_composer_device_1_t* pdev =
394       (struct cvd_hwc_composer_device_1_t*)dev;
395   pdev->vsync_data.procs = procs;
396   if (procs) {
397       std::vector<struct external_display_config_t> configs;
398       int res = GetExternalDisplayConfigs(&configs);
399       if (res == 0 && !configs.empty()) {
400           // configs will be used in the future
401           procs->hotplug(procs, HWC_DISPLAY_EXTERNAL, 1);
402       }
403   }
404 }
405 
cvd_hwc_query(hwc_composer_device_1_t * dev,int what,int * value)406 static int cvd_hwc_query(hwc_composer_device_1_t* dev, int what, int* value) {
407   struct cvd_hwc_composer_device_1_t* pdev =
408       (struct cvd_hwc_composer_device_1_t*)dev;
409 
410   switch (what) {
411     case HWC_BACKGROUND_LAYER_SUPPORTED:
412       // we support the background layer
413       value[0] = 0;
414       break;
415     case HWC_VSYNC_PERIOD:
416       value[0] = pdev->vsync_data.vsync_period_ns;
417       break;
418     default:
419       // unsupported query
420       ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what);
421       return -EINVAL;
422   }
423   return 0;
424 }
425 
cvd_hwc_event_control(hwc_composer_device_1_t *,int,int event,int)426 static int cvd_hwc_event_control(hwc_composer_device_1_t* /*dev*/, int /*dpy*/,
427                                  int event, int /*enabled*/) {
428   if (event == HWC_EVENT_VSYNC) {
429     return 0;
430   }
431   return -EINVAL;
432 }
433 
cvd_hwc_blank(hwc_composer_device_1_t *,int disp,int)434 static int cvd_hwc_blank(hwc_composer_device_1_t* /*dev*/, int disp, int /*blank*/) {
435   if (!IS_PRIMARY_DISPLAY(disp) && !IS_EXTERNAL_DISPLAY(disp)) return -EINVAL;
436   return 0;
437 }
438 
cvd_hwc_dump(hwc_composer_device_1_t * dev,char * buff,int buff_len)439 static void cvd_hwc_dump(hwc_composer_device_1_t* dev, char* buff, int buff_len) {
440   reinterpret_cast<cvd_hwc_composer_device_1_t*>(dev)->composer->Dump(buff,
441                                                                       buff_len);
442 }
443 
cvd_hwc_get_display_configs(hwc_composer_device_1_t *,int disp,uint32_t * configs,size_t * numConfigs)444 static int cvd_hwc_get_display_configs(hwc_composer_device_1_t* /*dev*/, int disp,
445                                        uint32_t* configs, size_t* numConfigs) {
446   if (*numConfigs == 0) return 0;
447 
448   if (IS_PRIMARY_DISPLAY(disp) || IS_EXTERNAL_DISPLAY(disp)) {
449     configs[0] = 0;
450     *numConfigs = 1;
451     return 0;
452   }
453 
454   return -EINVAL;
455 }
456 
cvd_hwc_attribute(struct cvd_hwc_composer_device_1_t * pdev,const uint32_t attribute)457 static int32_t cvd_hwc_attribute(struct cvd_hwc_composer_device_1_t* pdev,
458                                  const uint32_t attribute) {
459   switch (attribute) {
460     case HWC_DISPLAY_VSYNC_PERIOD:
461       return pdev->vsync_data.vsync_period_ns;
462     case HWC_DISPLAY_WIDTH:
463       return pdev->composer->x_res();
464     case HWC_DISPLAY_HEIGHT:
465       return pdev->composer->y_res();
466     case HWC_DISPLAY_DPI_X:
467       ALOGI("Reporting DPI_X of %d", pdev->composer->dpi());
468       // The number of pixels per thousand inches
469       return pdev->composer->dpi() * 1000;
470     case HWC_DISPLAY_DPI_Y:
471       ALOGI("Reporting DPI_Y of %d", pdev->composer->dpi());
472       // The number of pixels per thousand inches
473       return pdev->composer->dpi() * 1000;
474     default:
475       ALOGE("unknown display attribute %u", attribute);
476       return -EINVAL;
477   }
478 }
479 
cvd_hwc_get_display_attributes(hwc_composer_device_1_t * dev,int disp,uint32_t config __unused,const uint32_t * attributes,int32_t * values)480 static int cvd_hwc_get_display_attributes(hwc_composer_device_1_t* dev, int disp,
481                                           uint32_t config __unused,
482                                           const uint32_t* attributes,
483                                           int32_t* values) {
484   struct cvd_hwc_composer_device_1_t* pdev =
485       (struct cvd_hwc_composer_device_1_t*)dev;
486   if (!IS_PRIMARY_DISPLAY(disp) && !IS_EXTERNAL_DISPLAY(disp)) {
487     ALOGE("unknown display type %u", disp);
488     return -EINVAL;
489   }
490 
491   for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
492     values[i] = cvd_hwc_attribute(pdev, attributes[i]);
493   }
494 
495   return 0;
496 }
497 
cvd_hwc_close(hw_device_t * device)498 static int cvd_hwc_close(hw_device_t* device) {
499   struct cvd_hwc_composer_device_1_t* dev =
500       (struct cvd_hwc_composer_device_1_t*)device;
501   ALOGE("cvd_hwc_close");
502   pthread_kill(dev->vsync_data.vsync_thread, SIGTERM);
503   pthread_join(dev->vsync_data.vsync_thread, NULL);
504   delete dev->composer;
505   delete dev;
506   return 0;
507 }
508 
509 namespace cvd {
510 
cvd_hwc_open(std::unique_ptr<ScreenView> screen_view,const struct hw_module_t * module,const char * name,struct hw_device_t ** device)511 int cvd_hwc_open(std::unique_ptr<ScreenView> screen_view,
512                  const struct hw_module_t* module, const char* name,
513                  struct hw_device_t** device) {
514   ALOGI("%s", __FUNCTION__);
515   if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
516     ALOGE("%s called with bad name %s", __FUNCTION__, name);
517     return -EINVAL;
518   }
519 
520   cvd_hwc_composer_device_1_t* dev = new cvd_hwc_composer_device_1_t();
521   if (!dev) {
522     ALOGE("%s failed to allocate dev", __FUNCTION__);
523     return -ENOMEM;
524   }
525 
526   struct timespec rt;
527   if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
528     ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__,
529           strerror(errno));
530   }
531   dev->vsync_data.vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
532   dev->vsync_data.vsync_period_ns = 1e9 / screen_view->refresh_rate();
533 
534   dev->base.common.tag = HARDWARE_DEVICE_TAG;
535   dev->base.common.version = HWC_DEVICE_API_VERSION_1_1;
536   dev->base.common.module = const_cast<hw_module_t*>(module);
537   dev->base.common.close = cvd_hwc_close;
538 
539   dev->base.prepare = cvd_hwc_prepare;
540   dev->base.set = cvd_hwc_set;
541   dev->base.query = cvd_hwc_query;
542   dev->base.registerProcs = cvd_hwc_register_procs;
543   dev->base.dump = cvd_hwc_dump;
544   dev->base.blank = cvd_hwc_blank;
545   dev->base.eventControl = cvd_hwc_event_control;
546   dev->base.getDisplayConfigs = cvd_hwc_get_display_configs;
547   dev->base.getDisplayAttributes = cvd_hwc_get_display_attributes;
548 #ifdef GATHER_STATS
549   dev->composer = new cvd::StatsKeepingComposer<ComposerType>(
550       dev->vsync_data.vsync_base_timestamp, std::move(screen_view));
551 #else
552   dev->composer = new ComposerType(std::move(screen_view));
553 #endif
554 
555   if (!dev->composer) {
556     ALOGE("Failed to instantiate the composer object");
557     delete dev;
558     return -1;
559   }
560   int ret = pthread_create(&dev->vsync_data.vsync_thread, NULL,
561                            hwc_vsync_thread, &dev->vsync_data);
562   if (ret) {
563     ALOGE("failed to start vsync thread: %s", strerror(ret));
564     ret = -ret;
565     delete dev->composer;
566     delete dev;
567   } else {
568     *device = &dev->base.common;
569   }
570 
571   return ret;
572 }
573 
574 }  // namespace cvd
575