Add support for devserver latestbuild RPC for Android build

Add a new request argument os_type to differentiate requests for android builds
and ChromeOS builds. The value needs to be set to `android` for any request
related to Android build. If the value is not set or has other values, default
to ChromeOS builds.

BUG=chromium:512668
TEST=run devserver locally, and
curl http://localhost:8082/stage?target=shamu-userdebug\&build_id=2040953\&artifacts=test_zip\&branch=git_mnc-release\&os_type=android
curl http://localhost:8082/latestbuild?target=shamu-userdebug\&branch=git_mnc-release\&os_type=android

Change-Id: I32dd118c7c03c5899742cebe437d34b9cef984de
Reviewed-on: https://chromium-review.googlesource.com/308755
Commit-Ready: Dan Shi <[email protected]>
Tested-by: Dan Shi <[email protected]>
Reviewed-by: Simran Basi <[email protected]>
diff --git a/android_build.py b/android_build.py
index cae3b3b..597d8bb 100644
--- a/android_build.py
+++ b/android_build.py
@@ -118,3 +118,25 @@
       done = None
       while not done:
         _, done = downloader.next_chunk()
+
+  @classmethod
+  def GetLatestBuildID(cls, target, branch):
+    """Get the latest build ID for the given target and branch.
+
+    Args:
+      branch: branch of the desired build.
+      target: Target of the Android build, e.g., shamu-userdebug.
+
+    Returns:
+      Build id of the latest successful Android build for the given target and
+      branch, e.g., 2155602.
+    """
+    service_obj = cls._GetServiceObject()
+    builds = service_obj.build().list(
+        buildType='submitted', branch=branch, target=target, successful=True,
+        maxResults=1).execute()
+    if not builds or not builds['builds']:
+      raise AndroidBuildFetchError(
+          'Failed to locate build with branch %s and target %s.' %
+          (branch, target))
+    return builds['builds'][0]['buildId']
diff --git a/devserver.py b/devserver.py
index 74e1290..ebe46f2 100755
--- a/devserver.py
+++ b/devserver.py
@@ -203,6 +203,23 @@
           str(files).split(',') if files else [])
 
 
+def _is_android_build_request(kwargs):
+  """Check if a devserver call is for Android build, based on the arguments.
+
+  This method exams the request's arguments (os_type) to determine if the
+  request is for Android build. If os_type is set to `android`, returns True.
+  If os_type is not set or has other values, returns False.
+
+  Args:
+    kwargs: Keyword arguments for the request.
+
+  Returns:
+    True if the request is for Android build. False otherwise.
+  """
+  os_type = kwargs.get('os_type', None)
+  return os_type == 'android'
+
+
 def _get_downloader(kwargs):
   """Returns the downloader based on passed in arguments.
 
@@ -217,10 +234,7 @@
   if local_path:
     dl = downloader.LocalDownloader(updater.static_dir, local_path)
 
-  # Only Android build requires argument build_id. If it's not set, assume
-  # the download request is for ChromeOS.
-  build_id = kwargs.get('build_id', None)
-  if not build_id:
+  if not _is_android_build_request(kwargs):
     archive_url = kwargs.get('archive_url')
     if not archive_url and not local_path:
       raise DevServerError('Requires archive_url or local_path to be '
@@ -234,9 +248,11 @@
   elif not dl:
     target = kwargs.get('target', None)
     branch = kwargs.get('branch', None)
-    if not target or not branch:
+    build_id = kwargs.get('build_id', None)
+    if not target or not branch or not build_id:
       raise DevServerError(
-          'Both target and branch must be specified for Android build.')
+          'target, branch, build ID must all be specified for downloading '
+          'Android build.')
     dl = downloader.AndroidBuildDownloader(updater.static_dir, branch, build_id,
                                            target)
 
@@ -853,6 +869,16 @@
 
     if 'target' not in kwargs:
       raise common_util.DevServerHTTPError(500, 'Error: target= is required!')
+
+    if _is_android_build_request(kwargs):
+      branch = kwargs.get('branch', None)
+      target = kwargs.get('target', None)
+      if not target or not branch:
+        raise DevServerError(
+          'Both target and branch must be specified to query for the latest '
+          'Android build.')
+      return android_build.BuildAccessor.GetLatestBuildID(target, branch)
+
     try:
       return common_util.GetLatestBuildVersion(
           updater.static_dir, kwargs['target'],