diff --git a/Android.bp b/Android.bp
index 34e433b7dfc1575aef36d0fff300d8a5a30b4d4c..b10515db2b1981cd7fb044613b85901f892b4c1e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,10 +15,24 @@
 python_binary_host {
     name: "external_updater",
     main: "external_updater.py",
+    srcs: [
+        "external_updater.py",
+    ],
+    libs: [
+        "external_updater_lib",
+    ],
+}
+
+python_library_host {
+    name: "external_updater_lib",
     srcs: [
         "*.py",
         "metadata.proto",
     ],
+    exclude_srcs: [
+        "*_test.py",
+        "external_updater.py",
+    ],
     libs: [
         "python-symbol",
         "libprotobuf-python",
@@ -41,3 +55,13 @@ python_binary_host {
     },
 }
 
+python_test_host {
+    name: "external_updater_test",
+    main: "external_updater_test.py",
+    srcs: [
+        "external_updater_test.py",
+    ],
+    libs: [
+        "external_updater_lib",
+    ],
+}
diff --git a/external_updater_test.py b/external_updater_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c82f05fc665773721c7cfd7d9aaf666cf0c6863
--- /dev/null
+++ b/external_updater_test.py
@@ -0,0 +1,48 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Unit tests for external updater."""
+
+import unittest
+
+import github_archive_updater
+
+
+class ExternalUpdaterTest(unittest.TestCase):
+    """Unit tests for external updater."""
+
+    def test_url_selection(self):
+        """Tests that GithubArchiveUpdater can choose the right url."""
+        prefix = "https://github.com/author/project/"
+        urls = [
+            prefix + "releases/download/ver-1.0/ver-1.0-binary.zip",
+            prefix + "releases/download/ver-1.0/ver-1.0-binary.tar.gz",
+            prefix + "releases/download/ver-1.0/ver-1.0-src.zip",
+            prefix + "releases/download/ver-1.0/ver-1.0-src.tar.gz",
+            prefix + "archive/ver-1.0.zip",
+            prefix + "archive/ver-1.0.tar.gz",
+        ]
+
+        previous_url = prefix + "releases/download/ver-0.9/ver-0.9-src.tar.gz"
+        url = github_archive_updater.choose_best_url(urls, previous_url)
+        expected_url = prefix + "releases/download/ver-1.0/ver-1.0-src.tar.gz"
+        self.assertEqual(url, expected_url)
+
+        previous_url = prefix + "archive/ver-0.9.zip"
+        url = github_archive_updater.choose_best_url(urls, previous_url)
+        expected_url = prefix + "archive/ver-1.0.zip"
+        self.assertEqual(url, expected_url)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/git_utils.py b/git_utils.py
index 8e8f96d827f7ef2cda33a1589cd196787a586ad9..5743b8fa004172778dd10c8faa4b6dcdf89e292c 100644
--- a/git_utils.py
+++ b/git_utils.py
@@ -22,6 +22,7 @@ def _run(cmd, cwd):
     return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                           check=True, cwd=cwd)
 
+
 def fetch(proj_path, remote_names):
     """Runs git fetch.
 
@@ -31,6 +32,7 @@ def fetch(proj_path, remote_names):
     """
     _run(['git', 'fetch', '--multiple'] + remote_names, cwd=proj_path)
 
+
 def add_remote(proj_path, name, url):
     """Adds a git remote.
 
@@ -41,6 +43,7 @@ def add_remote(proj_path, name, url):
     """
     _run(['git', 'remote', 'add', name, url], cwd=proj_path)
 
+
 def list_remotes(proj_path):
     """Lists all Git remotes.
 
@@ -54,6 +57,7 @@ def list_remotes(proj_path):
     lines = out.stdout.decode('utf-8').splitlines()
     return dict([line.split()[0:2] for line in lines])
 
+
 def get_commits_ahead(proj_path, branch, base_branch):
     """Lists commits in `branch` but not `base_branch`."""
     out = _run(['git', 'rev-list', '--left-only',
@@ -61,11 +65,13 @@ def get_commits_ahead(proj_path, branch, base_branch):
                proj_path)
     return out.stdout.decode('utf-8').splitlines()
 
+
 def get_commit_time(proj_path, commit):
     """Gets commit time of one commit."""
     out = _run(['git', 'show', '-s', '--format=%ct', commit], cwd=proj_path)
     return datetime.datetime.fromtimestamp(int(out.stdout))
 
+
 def list_remote_branches(proj_path, remote_name):
     """Lists all branches for a remote."""
     out = _run(['git', 'branch', '-r'], cwd=proj_path)
diff --git a/github_archive_updater.py b/github_archive_updater.py
index e42e7b092121234f112d0bd9b26f7379003ad3c7..ea7ffc46909d66dfda407aa7e043fba8bd02c425 100644
--- a/github_archive_updater.py
+++ b/github_archive_updater.py
@@ -16,7 +16,6 @@
 
 import json
 import re
-import shutil
 import urllib.request
 
 import archive_utils
@@ -29,6 +28,39 @@ GITHUB_URL_PATTERN = (r'^https:\/\/github.com\/([-\w]+)\/([-\w]+)\/' +
 GITHUB_URL_RE = re.compile(GITHUB_URL_PATTERN)
 
 
+def _edit_distance(str1, str2):
+    prev = list(range(0, len(str2) + 1))
+    for i, chr1 in enumerate(str1):
+        cur = [i + 1]
+        for j, chr2 in enumerate(str2):
+            if chr1 == chr2:
+                cur.append(prev[j])
+            else:
+                cur.append(min(prev[j + 1], prev[j], cur[j]) + 1)
+        prev = cur
+    return prev[len(str2)]
+
+
+def choose_best_url(urls, previous_url):
+    """Returns the best url to download from a list of candidate urls.
+
+    This function calculates similarity between previous url and each of new
+    urls. And returns the one best matches previous url.
+
+    Similarity is measured by editing distance.
+
+    Args:
+        urls: Array of candidate urls.
+        previous_url: String of the url used previously.
+
+    Returns:
+        One url from `urls`.
+    """
+    return min(urls, default=None,
+               key=lambda url: _edit_distance(
+                   url, previous_url))
+
+
 class GithubArchiveUpdater():
     """Updater for archives from GitHub.
 
@@ -98,18 +130,18 @@ class GithubArchiveUpdater():
         """
 
         supported_assets = [
-            a for a in self.data['assets']
+            a['browser_download_url'] for a in self.data['assets']
             if archive_utils.is_supported_archive(a['browser_download_url'])]
 
-        # Finds the minimum sized archive to download.
-        minimum_asset = min(
-            supported_assets, key=lambda asset: asset['size'], default=None)
-        if minimum_asset is not None:
-            latest_url = minimum_asset.get('browser_download_url')
-        else:
-            # Guess the tarball url for source code.
-            latest_url = 'https://github.com/{}/{}/archive/{}.tar.gz'.format(
-                self.owner, self.repo, self.data.get('tag_name'))
+        # Adds source code urls.
+        supported_assets.append(
+            'https://github.com/{}/{}/archive/{}.tar.gz'.format(
+                self.owner, self.repo, self.data.get('tag_name')))
+        supported_assets.append(
+            'https://github.com/{}/{}/archive/{}.zip'.format(
+                self.owner, self.repo, self.data.get('tag_name')))
+
+        latest_url = choose_best_url(supported_assets, self.old_url.value)
 
         temporary_dir = None
         try: