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