• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Facade to access the display-related functionality."""
6
7import logging
8import multiprocessing
9import numpy
10import os
11import re
12import shutil
13import time
14import json
15from autotest_lib.client.bin import utils
16from autotest_lib.client.common_lib import error
17from autotest_lib.client.common_lib import utils as common_utils
18from autotest_lib.client.common_lib.cros import retry
19from autotest_lib.client.cros import constants
20from autotest_lib.client.cros.graphics import graphics_utils
21from autotest_lib.client.cros.multimedia import facade_resource
22from autotest_lib.client.cros.multimedia import image_generator
23from autotest_lib.client.cros.power import sys_power
24from telemetry.internal.browser import web_contents
25
26class TimeoutException(Exception):
27    """Timeout Exception class."""
28    pass
29
30
31_FLAKY_CALL_RETRY_TIMEOUT_SEC = 60
32_FLAKY_DISPLAY_CALL_RETRY_DELAY_SEC = 2
33
34_retry_display_call = retry.retry(
35        (KeyError, error.CmdError),
36        timeout_min=_FLAKY_CALL_RETRY_TIMEOUT_SEC / 60.0,
37        delay_sec=_FLAKY_DISPLAY_CALL_RETRY_DELAY_SEC)
38
39
40class DisplayFacadeNative(object):
41    """Facade to access the display-related functionality.
42
43    The methods inside this class only accept Python native types.
44    """
45
46    CALIBRATION_IMAGE_PATH = '/tmp/calibration.png'
47    MINIMUM_REFRESH_RATE_EXPECTED = 25.0
48    DELAY_TIME = 3
49    MAX_TYPEC_PORT = 6
50
51    def __init__(self, resource):
52        """Initializes a DisplayFacadeNative.
53
54        @param resource: A FacadeResource object.
55        """
56        self._resource = resource
57        self._image_generator = image_generator.ImageGenerator()
58
59
60    @facade_resource.retry_chrome_call
61    def get_display_info(self):
62        """Gets the display info from Chrome.system.display API.
63
64        @return array of dict for display info.
65        """
66        extension = self._resource.get_extension(
67                constants.DISPLAY_TEST_EXTENSION)
68        extension.ExecuteJavaScript('window.__display_info = null;')
69        extension.ExecuteJavaScript(
70                "chrome.system.display.getInfo(function(info) {"
71                "window.__display_info = info;})")
72        utils.wait_for_value(lambda: (
73                extension.EvaluateJavaScript("window.__display_info") != None),
74                expected_value=True)
75        return extension.EvaluateJavaScript("window.__display_info")
76
77
78    @facade_resource.retry_chrome_call
79    def get_window_info(self):
80        """Gets the current window info from Chrome.system.window API.
81
82        @return a dict for the information of the current window.
83        """
84        extension = self._resource.get_extension()
85        extension.ExecuteJavaScript('window.__window_info = null;')
86        extension.ExecuteJavaScript(
87                "chrome.windows.getCurrent(function(info) {"
88                "window.__window_info = info;})")
89        utils.wait_for_value(lambda: (
90                extension.EvaluateJavaScript("window.__window_info") != None),
91                expected_value=True)
92        return extension.EvaluateJavaScript("window.__window_info")
93
94
95    @facade_resource.retry_chrome_call
96    def create_window(self, url='chrome://newtab'):
97        """Creates a new window from chrome.windows.create API.
98
99        @param url: Optional URL for the new window.
100
101        @return Identifier for the new window.
102
103        @raise TimeoutException if it fails.
104        """
105        extension = self._resource.get_extension()
106
107        extension.ExecuteJavaScript(
108                """
109                var __new_window_id = null;
110                chrome.windows.create(
111                        {url: '%s'},
112                        function(win) {
113                            __new_window_id = win.id});
114                """ % (url)
115        )
116        extension.WaitForJavaScriptCondition(
117                "__new_window_id !== null",
118                timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT)
119
120        return extension.EvaluateJavaScript("__new_window_id")
121
122
123    @facade_resource.retry_chrome_call
124    def update_window(self, window_id, state=None, bounds=None):
125        """Updates an existing window using the chrome.windows.update API.
126
127        @param window_id: Identifier for the window to update.
128        @param state: Optional string to set the state such as 'normal',
129                      'maximized', or 'fullscreen'.
130        @param bounds: Optional dictionary with keys top, left, width, and
131                       height to reposition the window.
132
133        @return True if success.
134
135        @raise TimeoutException if it fails.
136        """
137        extension = self._resource.get_extension()
138        params = {}
139
140        if state:
141            params['state'] = state
142        if bounds:
143            params['top'] = bounds['top']
144            params['left'] = bounds['left']
145            params['width'] = bounds['width']
146            params['height'] = bounds['height']
147
148        if not params:
149            logging.info('Nothing to update for window_id={}'.format(window_id))
150            return True
151
152        extension.ExecuteJavaScript(
153                """
154                var __status = 'Running';
155                chrome.windows.update(%d, %s,
156                        function(win) {
157                            __status = 'Done'});
158                """ % (window_id, json.dumps(params))
159        )
160        extension.WaitForJavaScriptCondition(
161                "__status == 'Done'",
162                timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT)
163
164        return True
165
166
167    def _get_display_by_id(self, display_id):
168        """Gets a display by ID.
169
170        @param display_id: id of the display.
171
172        @return: A dict of various display info.
173        """
174        for display in self.get_display_info():
175            if display['id'] == display_id:
176                return display
177        raise RuntimeError('Cannot find display ' + display_id)
178
179
180    def get_display_modes(self, display_id):
181        """Gets all the display modes for the specified display.
182
183        @param display_id: id of the display to get modes from.
184
185        @return: A list of DisplayMode dicts.
186        """
187        display = self._get_display_by_id(display_id)
188        return display['modes']
189
190
191    def get_display_rotation(self, display_id):
192        """Gets the display rotation for the specified display.
193
194        @param display_id: id of the display to get modes from.
195
196        @return: Degree of rotation.
197        """
198        display = self._get_display_by_id(display_id)
199        return display['rotation']
200
201
202    def get_display_notifications(self):
203        """Gets the display notifications
204
205        @return: Returns a list of display related notifications only.
206        """
207        display_notifications = []
208        for notification in self._resource.get_visible_notifications():
209            if notification['id'] == 'chrome://settings/display':
210                display_notifications.append(notification)
211        return display_notifications
212
213
214    def set_display_rotation(self, display_id, rotation,
215                             delay_before_rotation=0, delay_after_rotation=0):
216        """Sets the display rotation for the specified display.
217
218        @param display_id: id of the display to get modes from.
219        @param rotation: degree of rotation
220        @param delay_before_rotation: time in second for delay before rotation
221        @param delay_after_rotation: time in second for delay after rotation
222        """
223        time.sleep(delay_before_rotation)
224        extension = self._resource.get_extension(
225                constants.DISPLAY_TEST_EXTENSION)
226        extension.ExecuteJavaScript(
227                """
228                window.__set_display_rotation_has_error = null;
229                chrome.system.display.setDisplayProperties('%(id)s',
230                    {"rotation": %(rotation)d}, () => {
231                    if (chrome.runtime.lastError) {
232                        console.error('Failed to set display rotation',
233                            chrome.runtime.lastError);
234                        window.__set_display_rotation_has_error = "failure";
235                    } else {
236                        window.__set_display_rotation_has_error = "success";
237                    }
238                });
239                """
240                % {'id': display_id, 'rotation': rotation}
241        )
242        utils.wait_for_value(lambda: (
243                extension.EvaluateJavaScript(
244                    'window.__set_display_rotation_has_error') != None),
245                expected_value=True)
246        time.sleep(delay_after_rotation)
247        result = extension.EvaluateJavaScript(
248                'window.__set_display_rotation_has_error')
249        if result != 'success':
250            raise RuntimeError('Failed to set display rotation: %r' % result)
251
252
253    def get_available_resolutions(self, display_id):
254        """Gets the resolutions from the specified display.
255
256        @return a list of (width, height) tuples.
257        """
258        display = self._get_display_by_id(display_id)
259        modes = display['modes']
260        if 'widthInNativePixels' not in modes[0]:
261            raise RuntimeError('Cannot find widthInNativePixels attribute')
262        if display['isInternal']:
263            logging.info("Getting resolutions of internal display")
264            return list(set([(mode['width'], mode['height']) for mode in
265                             modes]))
266        return list(set([(mode['widthInNativePixels'],
267                          mode['heightInNativePixels']) for mode in modes]))
268
269
270    def has_internal_display(self):
271        """Returns whether the device has an internal display.
272
273        @return whether the device has an internal display
274        """
275        return len([d for d in self.get_display_info() if d['isInternal']]) > 0
276
277
278    def get_internal_display_id(self):
279        """Gets the internal display id.
280
281        @return the id of the internal display.
282        """
283        for display in self.get_display_info():
284            if display['isInternal']:
285                return display['id']
286        raise RuntimeError('Cannot find internal display')
287
288
289    def get_first_external_display_id(self):
290        """Gets the first external display id.
291
292        @return the id of the first external display; -1 if not found.
293        """
294        # Get the first external and enabled display
295        for display in self.get_display_info():
296            if display['isEnabled'] and not display['isInternal']:
297                return display['id']
298        return -1
299
300
301    def set_resolution(self, display_id, width, height, timeout=3):
302        """Sets the resolution of the specified display.
303
304        @param display_id: id of the display to set resolution for.
305        @param width: width of the resolution
306        @param height: height of the resolution
307        @param timeout: maximal time in seconds waiting for the new resolution
308                to settle in.
309        @raise TimeoutException when the operation is timed out.
310        """
311
312        extension = self._resource.get_extension(
313                constants.DISPLAY_TEST_EXTENSION)
314        extension.ExecuteJavaScript(
315                """
316                window.__set_resolution_progress = null;
317                chrome.system.display.getInfo((info_array) => {
318                    var mode;
319                    for (var info of info_array) {
320                        if (info['id'] == '%(id)s') {
321                            for (var m of info['modes']) {
322                                if (m['width'] == %(width)d &&
323                                    m['height'] == %(height)d) {
324                                    mode = m;
325                                    break;
326                                }
327                            }
328                            break;
329                        }
330                    }
331                    if (mode === undefined) {
332                        console.error('Failed to select the resolution ' +
333                            '%(width)dx%(height)d');
334                        window.__set_resolution_progress = "mode not found";
335                        return;
336                    }
337
338                    chrome.system.display.setDisplayProperties('%(id)s',
339                        {'displayMode': mode}, () => {
340                            if (chrome.runtime.lastError) {
341                                window.__set_resolution_progress = "failed: " +
342                                    chrome.runtime.lastError.message;
343                            } else {
344                                window.__set_resolution_progress = "succeeded";
345                            }
346                        }
347                    );
348                });
349                """
350                % {'id': display_id, 'width': width, 'height': height}
351        )
352        utils.wait_for_value(lambda: (
353                extension.EvaluateJavaScript(
354                    'window.__set_resolution_progress') != None),
355                expected_value=True)
356        result = extension.EvaluateJavaScript(
357                'window.__set_resolution_progress')
358        if result != 'succeeded':
359            raise RuntimeError('Failed to set resolution: %r' % result)
360
361
362    @_retry_display_call
363    def get_external_resolution(self):
364        """Gets the resolution of the external screen.
365
366        @return The resolution tuple (width, height)
367        """
368        return graphics_utils.get_external_resolution()
369
370    def get_internal_resolution(self):
371        """Gets the resolution of the internal screen.
372
373        @return The resolution tuple (width, height) or None if internal screen
374                is not available
375        """
376        for display in self.get_display_info():
377            if display['isInternal']:
378                bounds = display['bounds']
379                return (bounds['width'], bounds['height'])
380        return None
381
382
383    def set_content_protection(self, state):
384        """Sets the content protection of the external screen.
385
386        @param state: One of the states 'Undesired', 'Desired', or 'Enabled'
387        """
388        connector = self.get_external_connector_name()
389        graphics_utils.set_content_protection(connector, state)
390
391
392    def get_content_protection(self):
393        """Gets the state of the content protection.
394
395        @param output: The output name as a string.
396        @return: A string of the state, like 'Undesired', 'Desired', or 'Enabled'.
397                 False if not supported.
398        """
399        connector = self.get_external_connector_name()
400        return graphics_utils.get_content_protection(connector)
401
402
403    def get_external_crtc_id(self):
404        """Gets the external crtc.
405
406        @return The id of the external crtc."""
407        return graphics_utils.get_external_crtc_id()
408
409
410    def get_internal_crtc_id(self):
411        """Gets the internal crtc.
412
413        @retrun The id of the internal crtc."""
414        return graphics_utils.get_internal_crtc_id()
415
416
417    def take_internal_screenshot(self, path):
418        """Takes internal screenshot.
419
420        @param path: path to image file.
421        """
422        self.take_screenshot_crtc(path, self.get_internal_crtc_id())
423
424
425    def take_external_screenshot(self, path):
426        """Takes external screenshot.
427
428        @param path: path to image file.
429        """
430        self.take_screenshot_crtc(path, self.get_external_crtc_id())
431
432
433    def take_screenshot_crtc(self, path, id):
434        """Captures the DUT screenshot, use id for selecting screen.
435
436        @param path: path to image file.
437        @param id: The id of the crtc to screenshot.
438        """
439
440        graphics_utils.take_screenshot_crop(path, crtc_id=id)
441        return True
442
443
444    def save_calibration_image(self, path):
445        """Save the calibration image to the given path.
446
447        @param path: path to image file.
448        """
449        shutil.copy(self.CALIBRATION_IMAGE_PATH, path)
450        return True
451
452
453    def take_tab_screenshot(self, output_path, url_pattern=None):
454        """Takes a screenshot of the tab specified by the given url pattern.
455
456        @param output_path: A path of the output file.
457        @param url_pattern: A string of url pattern used to search for tabs.
458                            Default is to look for .svg image.
459        """
460        if url_pattern is None:
461            # If no URL pattern is provided, defaults to capture the first
462            # tab that shows SVG image.
463            url_pattern = '.svg'
464
465        tabs = self._resource.get_tabs()
466        for i in xrange(0, len(tabs)):
467            if url_pattern in tabs[i].url:
468                data = tabs[i].Screenshot(timeout=5)
469                # Flip the colors from BGR to RGB.
470                data = numpy.fliplr(data.reshape(-1, 3)).reshape(data.shape)
471                data.tofile(output_path)
472                break
473        return True
474
475
476    def toggle_mirrored(self):
477        """Toggles mirrored."""
478        graphics_utils.screen_toggle_mirrored()
479        return True
480
481
482    def hide_cursor(self):
483        """Hides mouse cursor."""
484        graphics_utils.hide_cursor()
485        return True
486
487
488    def hide_typing_cursor(self):
489        """Hides typing cursor."""
490        graphics_utils.hide_typing_cursor()
491        return True
492
493
494    def is_mirrored_enabled(self):
495        """Checks the mirrored state.
496
497        @return True if mirrored mode is enabled.
498        """
499        return bool(self.get_display_info()[0]['mirroringSourceId'])
500
501
502    def set_mirrored(self, is_mirrored):
503        """Sets mirrored mode.
504
505        @param is_mirrored: True or False to indicate mirrored state.
506        @return True if success, False otherwise.
507        """
508        if self.is_mirrored_enabled() == is_mirrored:
509            return True
510
511        retries = 4
512        while retries > 0:
513            self.toggle_mirrored()
514            result = utils.wait_for_value(self.is_mirrored_enabled,
515                                          expected_value=is_mirrored,
516                                          timeout_sec=3)
517            if result == is_mirrored:
518                return True
519            retries -= 1
520        return False
521
522
523    def is_display_primary(self, internal=True):
524        """Checks if internal screen is primary display.
525
526        @param internal: is internal/external screen primary status requested
527        @return boolean True if internal display is primary.
528        """
529        for info in self.get_display_info():
530            if info['isInternal'] == internal and info['isPrimary']:
531                return True
532        return False
533
534
535    def suspend_resume(self, suspend_time=10):
536        """Suspends the DUT for a given time in second.
537
538        @param suspend_time: Suspend time in second.
539        """
540        sys_power.do_suspend(suspend_time)
541        return True
542
543
544    def suspend_resume_bg(self, suspend_time=10):
545        """Suspends the DUT for a given time in second in the background.
546
547        @param suspend_time: Suspend time in second.
548        """
549        process = multiprocessing.Process(target=self.suspend_resume,
550                                          args=(suspend_time,))
551        process.start()
552        return True
553
554
555    @_retry_display_call
556    def get_external_connector_name(self):
557        """Gets the name of the external output connector.
558
559        @return The external output connector name as a string, if any.
560                Otherwise, return False.
561        """
562        return graphics_utils.get_external_connector_name()
563
564
565    def get_internal_connector_name(self):
566        """Gets the name of the internal output connector.
567
568        @return The internal output connector name as a string, if any.
569                Otherwise, return False.
570        """
571        return graphics_utils.get_internal_connector_name()
572
573
574    def wait_external_display_connected(self, display):
575        """Waits for the specified external display to be connected.
576
577        @param display: The display name as a string, like 'HDMI1', or
578                        False if no external display is expected.
579        @return: True if display is connected; False otherwise.
580        """
581        result = utils.wait_for_value(self.get_external_connector_name,
582                                      expected_value=display)
583        return result == display
584
585
586    @facade_resource.retry_chrome_call
587    def move_to_display(self, display_id):
588        """Moves the current window to the indicated display.
589
590        @param display_id: The id of the indicated display.
591        @return True if success.
592
593        @raise TimeoutException if it fails.
594        """
595        display_info = self._get_display_by_id(display_id)
596        if not display_info['isEnabled']:
597            raise RuntimeError('Cannot find the indicated display')
598        target_bounds = display_info['bounds']
599
600        extension = self._resource.get_extension()
601        # If the area of bounds is empty (here we achieve this by setting
602        # width and height to zero), the window_sizer will automatically
603        # determine an area which is visible and fits on the screen.
604        # For more details, see chrome/browser/ui/window_sizer.cc
605        # Without setting state to 'normal', if the current state is
606        # 'minimized', 'maximized' or 'fullscreen', the setting of
607        # 'left', 'top', 'width' and 'height' will be ignored.
608        # For more details, see chrome/browser/extensions/api/tabs/tabs_api.cc
609        extension.ExecuteJavaScript(
610                """
611                var __status = 'Running';
612                chrome.windows.update(
613                        chrome.windows.WINDOW_ID_CURRENT,
614                        {left: %d, top: %d, width: 0, height: 0,
615                         state: 'normal'},
616                        function(info) {
617                            if (info.left == %d && info.top == %d &&
618                                info.state == 'normal')
619                                __status = 'Done'; });
620                """
621                % (target_bounds['left'], target_bounds['top'],
622                   target_bounds['left'], target_bounds['top'])
623        )
624        extension.WaitForJavaScriptCondition(
625                "__status == 'Done'",
626                timeout=web_contents.DEFAULT_WEB_CONTENTS_TIMEOUT)
627        return True
628
629
630    def is_fullscreen_enabled(self):
631        """Checks the fullscreen state.
632
633        @return True if fullscreen mode is enabled.
634        """
635        return self.get_window_info()['state'] == 'fullscreen'
636
637
638    def set_fullscreen(self, is_fullscreen):
639        """Sets the current window to full screen.
640
641        @param is_fullscreen: True or False to indicate fullscreen state.
642        @return True if success, False otherwise.
643        """
644        extension = self._resource.get_extension()
645        if not extension:
646            raise RuntimeError('Autotest extension not found')
647
648        if is_fullscreen:
649            window_state = "fullscreen"
650        else:
651            window_state = "normal"
652        extension.ExecuteJavaScript(
653                """
654                var __status = 'Running';
655                chrome.windows.update(
656                        chrome.windows.WINDOW_ID_CURRENT,
657                        {state: '%s'},
658                        function() { __status = 'Done'; });
659                """
660                % window_state)
661        utils.wait_for_value(lambda: (
662                extension.EvaluateJavaScript('__status') == 'Done'),
663                expected_value=True)
664        return self.is_fullscreen_enabled() == is_fullscreen
665
666
667    def load_url(self, url):
668        """Loads the given url in a new tab. The new tab will be active.
669
670        @param url: The url to load as a string.
671        @return a str, the tab descriptor of the opened tab.
672        """
673        return self._resource.load_url(url)
674
675
676    def load_calibration_image(self, resolution):
677        """Opens a new tab and loads a full screen calibration
678           image from the HTTP server.
679
680        @param resolution: A tuple (width, height) of resolution.
681        @return a str, the tab descriptor of the opened tab.
682        """
683        path = self.CALIBRATION_IMAGE_PATH
684        self._image_generator.generate_image(resolution[0], resolution[1], path)
685        os.chmod(path, 0644)
686        tab_descriptor = self.load_url('file://%s' % path)
687        return tab_descriptor
688
689
690    def load_color_sequence(self, tab_descriptor, color_sequence):
691        """Displays a series of colors on full screen on the tab.
692        tab_descriptor is returned by any open tab API of display facade.
693        e.g.,
694        tab_descriptor = load_url('about:blank')
695        load_color_sequence(tab_descriptor, color)
696
697        @param tab_descriptor: Indicate which tab to test.
698        @param color_sequence: An integer list for switching colors.
699        @return A list of the timestamp for each switch.
700        """
701        tab = self._resource.get_tab_by_descriptor(tab_descriptor)
702        color_sequence_for_java_script = (
703                'var color_sequence = [' +
704                ','.join("'#%06X'" % x for x in color_sequence) +
705                '];')
706        # Paints are synchronized to the fresh rate of the screen by
707        # window.requestAnimationFrame.
708        tab.ExecuteJavaScript(color_sequence_for_java_script + """
709            function render(timestamp) {
710                window.timestamp_list.push(timestamp);
711                if (window.count < color_sequence.length) {
712                    document.body.style.backgroundColor =
713                            color_sequence[count];
714                    window.count++;
715                    window.requestAnimationFrame(render);
716                }
717            }
718            window.count = 0;
719            window.timestamp_list = [];
720            window.requestAnimationFrame(render);
721            """)
722
723        # Waiting time is decided by following concerns:
724        # 1. MINIMUM_REFRESH_RATE_EXPECTED: the minimum refresh rate
725        #    we expect it to be. Real refresh rate is related to
726        #    not only hardware devices but also drivers and browsers.
727        #    Most graphics devices support at least 60fps for a single
728        #    monitor, and under mirror mode, since the both frames
729        #    buffers need to be updated for an input frame, the refresh
730        #    rate will decrease by half, so here we set it to be a
731        #    little less than 30 (= 60/2) to make it more tolerant.
732        # 2. DELAY_TIME: extra wait time for timeout.
733        tab.WaitForJavaScriptCondition(
734                'window.count == color_sequence.length',
735                timeout=(
736                    (len(color_sequence) / self.MINIMUM_REFRESH_RATE_EXPECTED)
737                    + self.DELAY_TIME))
738        return tab.EvaluateJavaScript("window.timestamp_list")
739
740
741    def close_tab(self, tab_descriptor):
742        """Disables fullscreen and closes the tab of the given tab descriptor.
743        tab_descriptor is returned by any open tab API of display facade.
744        e.g.,
745        1.
746        tab_descriptor = load_url(url)
747        close_tab(tab_descriptor)
748
749        2.
750        tab_descriptor = load_calibration_image(resolution)
751        close_tab(tab_descriptor)
752
753        @param tab_descriptor: Indicate which tab to be closed.
754        """
755        if tab_descriptor:
756            # set_fullscreen(False) is necessary here because currently there
757            # is a bug in tabs.Close(). If the current state is fullscreen and
758            # we call close_tab() without setting state back to normal, it will
759            # cancel fullscreen mode without changing system configuration, and
760            # so that the next time someone calls set_fullscreen(True), the
761            # function will find that current state is already 'fullscreen'
762            # (though it is not) and do nothing, which will break all the
763            # following tests.
764            self.set_fullscreen(False)
765            self._resource.close_tab(tab_descriptor)
766        else:
767            logging.error('close_tab: not a valid tab_descriptor')
768
769        return True
770
771
772    def reset_connector_if_applicable(self, connector_type):
773        """Resets Type-C video connector from host end if applicable.
774
775        It's the workaround sequence since sometimes Type-C dongle becomes
776        corrupted and needs to be re-plugged.
777
778        @param connector_type: A string, like "VGA", "DVI", "HDMI", or "DP".
779        """
780        if connector_type != 'HDMI' and connector_type != 'DP':
781            return
782        # Decide if we need to add --name=cros_pd
783        usbpd_command = 'ectool --name=cros_pd usbpd'
784        try:
785            common_utils.run('%s 0' % usbpd_command)
786        except error.CmdError:
787            usbpd_command = 'ectool usbpd'
788
789        port = 0
790        while port < self.MAX_TYPEC_PORT:
791            # We use usbpd to get Role information and then power cycle the
792            # SRC one.
793            command = '%s %d' % (usbpd_command, port)
794            try:
795                output = common_utils.run(command).stdout
796                if re.compile('Role.*SRC').search(output):
797                    logging.info('power-cycle Type-C port %d', port)
798                    common_utils.run('%s sink' % command)
799                    common_utils.run('%s auto' % command)
800                port += 1
801            except error.CmdError:
802                break
803