diff --git a/.travis.yml b/.travis.yml
index dbc3cc812d4b68c1e99457825bc90c588c902519..3841a78c06f966cb2547aa24dd51c270f2e59c69 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -53,29 +53,22 @@ matrix:
         - CC=aarch64-linux-gnu-gcc go run build/ci.go install -arch arm64
         - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
 
-    # This builder does the OSX Azure uploads
+    # This builder does the OSX Azure, Android Maven and Azure and iOS CocoaPods and Azure uploads
     - os: osx
       go: 1.7
       env:
         - azure-osx
+        - mobile
       script:
         - go run build/ci.go install
         - go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -upload gethstore/builds
 
-    # This builder does the mobile builds (and nothing else)
-    - os: osx
-      go: 1.7
-      script:
+        # Build the Android archives and upload them to Maven Central
         - brew update
-        - brew install android-sdk
+        - brew install android-sdk maven
         - export ANDROID_HOME=/usr/local/opt/android-sdk
         - echo "y" | android update sdk --no-ui --filter platform
-        - go get github.com/tools/godep
-        - godep restore
-        - go get golang.org/x/mobile/cmd/gomobile
-        - gomobile init
-        - gomobile bind --target=android --javapkg=org.ethereum -v github.com/ethereum/go-ethereum/mobile
-        - gomobile bind --target=ios --tags=ios -v github.com/ethereum/go-ethereum/mobile
+        - go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds
 
 install:
   - go get golang.org/x/tools/cmd/cover
diff --git a/build/ci.go b/build/ci.go
index 691f5233edaf878cb5b348f28fe8be2c9f7c6a48..cc99e35219a9a9e927fde48308df147836b89bf1 100644
--- a/build/ci.go
+++ b/build/ci.go
@@ -29,6 +29,7 @@ Available commands are:
    importkeys                                                                                -- imports signing keys from env
    debsrc     [ -signer key-id ] [ -upload dest ]                                            -- creates a debian source package
    nsis                                                                                      -- creates a Windows NSIS installer
+   aar        [ -sign key-id ] [ -upload dest ]                                              -- creates an android archive
    xgo        [ options ]                                                                    -- cross builds according to options
 
 For all commands, -n prevents execution of external programs (dry run mode).
@@ -37,6 +38,7 @@ For all commands, -n prevents execution of external programs (dry run mode).
 package main
 
 import (
+	"bufio"
 	"bytes"
 	"encoding/base64"
 	"flag"
@@ -48,6 +50,7 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
+	"regexp"
 	"runtime"
 	"strings"
 	"time"
@@ -125,6 +128,8 @@ func main() {
 		doDebianSource(os.Args[2:])
 	case "nsis":
 		doWindowsInstaller(os.Args[2:])
+	case "aar":
+		doAndroidArchive(os.Args[2:])
 	case "xgo":
 		doXgo(os.Args[2:])
 	default:
@@ -331,6 +336,9 @@ func archiveBasename(arch string, env build.Environment) string {
 	if arch == "arm" {
 		platform += os.Getenv("GOARM")
 	}
+	if arch == "android" {
+		platform = "android-all"
+	}
 	archive := platform + "-" + build.VERSION()
 	if isUnstableBuild(env) {
 		archive += "-unstable"
@@ -630,6 +638,125 @@ func doWindowsInstaller(cmdline []string) {
 	if err := archiveUpload(installer, *upload, *signer); err != nil {
 		log.Fatal(err)
 	}
+
+// Android archives
+
+func doAndroidArchive(cmdline []string) {
+	var (
+		signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`)
+		deploy = flag.String("deploy", "", `Where to upload the deploy the archive (usually "https://oss.sonatype.org")`)
+		upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
+	)
+	flag.CommandLine.Parse(cmdline)
+	env := build.Env()
+
+	// Build the Android archive and Maven resources
+	build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile"))
+	build.MustRun(gomobileTool("init"))
+	build.MustRun(gomobileTool("bind", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
+
+	meta := newMavenMetadata(env)
+	build.Render("build/mvn.pom", meta.PackageString()+".pom", 0755, meta)
+
+	// Skip Maven deploy and Azure upload for PR builds
+	maybeSkipArchive(env)
+
+	// Sign and upload all the artifacts to Maven Central
+	os.Rename("geth.aar", meta.PackageString()+".aar")
+	if *signer != "" && *deploy != "" {
+		// Import the signing key into the local GPG instance
+		if b64key := os.Getenv(*signer); b64key != "" {
+			key, err := base64.StdEncoding.DecodeString(b64key)
+			if err != nil {
+				log.Fatalf("invalid base64 %s", *signer)
+			}
+			gpg := exec.Command("gpg", "--import")
+			gpg.Stdin = bytes.NewReader(key)
+			build.MustRun(gpg)
+		}
+		// Upload the artifacts to Sonatype and/or Maven Central
+		repo := *deploy + "/service/local/staging/deploy/maven2"
+		if meta.Unstable {
+			repo = *deploy + "/content/repositories/snapshots"
+		}
+		build.MustRunCommand("mvn", "gpg:sign-and-deploy-file",
+			"-settings=build/mvn.settings", "-Durl="+repo, "-DrepositoryId=ossrh",
+			"-DpomFile="+meta.PackageString()+".pom", "-Dfile="+meta.PackageString()+".aar")
+	}
+	// Sign and upload the archive to Azure
+	archive := "geth-" + archiveBasename("android", env) + ".aar"
+	os.Rename(meta.PackageString()+".aar", archive)
+
+	if err := archiveUpload(archive, *upload, *signer); err != nil {
+		log.Fatal(err)
+	}
+}
+
+func gomobileTool(subcmd string, args ...string) *exec.Cmd {
+	cmd := exec.Command(filepath.Join(GOBIN, "gomobile"), subcmd)
+	cmd.Args = append(cmd.Args, args...)
+	cmd.Env = []string{
+		"GOPATH=" + build.GOPATH(),
+	}
+	for _, e := range os.Environ() {
+		if strings.HasPrefix(e, "GOPATH=") {
+			continue
+		}
+		cmd.Env = append(cmd.Env, e)
+	}
+	return cmd
+}
+
+type mavenMetadata struct {
+	Env          build.Environment
+	Version      string
+	Unstable     bool
+	Contributors []mavenContributor
+}
+
+type mavenContributor struct {
+	Name  string
+	Email string
+}
+
+func newMavenMetadata(env build.Environment) mavenMetadata {
+	// Collect the list of authors from the repo root
+	contribs := []mavenContributor{}
+	if authors, err := os.Open("AUTHORS"); err == nil {
+		defer authors.Close()
+
+		scanner := bufio.NewScanner(authors)
+		for scanner.Scan() {
+			// Skip any whitespace from the authors list
+			line := strings.TrimSpace(scanner.Text())
+			if line == "" || line[0] == '#' {
+				continue
+			}
+			// Split the author and insert as a contributor
+			re := regexp.MustCompile("([^<]+) <(.+>)")
+			parts := re.FindStringSubmatch(line)
+			if len(parts) == 3 {
+				contribs = append(contribs, mavenContributor{Name: parts[1], Email: parts[2]})
+			}
+		}
+	}
+	return mavenMetadata{
+		Env:          env,
+		Version:      build.VERSION(),
+		Unstable:     isUnstableBuild(env),
+		Contributors: contribs,
+	}
+}
+
+func (meta mavenMetadata) VersionString() string {
+	if meta.Unstable {
+		return meta.Version + "-SNAPSHOT"
+	}
+	return meta.Version
+}
+
+func (meta mavenMetadata) PackageString() string {
+	return "geth-" + meta.VersionString()
 }
 
 // Cross compilation
diff --git a/build/mvn.pom b/build/mvn.pom
new file mode 100644
index 0000000000000000000000000000000000000000..0092268765604d211ef4c6455831faa84490d821
--- /dev/null
+++ b/build/mvn.pom
@@ -0,0 +1,57 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.ethereum</groupId>
+  <artifactId>geth</artifactId>
+  <version>{{.VersionString}}</version>
+  <packaging>aar</packaging>
+
+  <name>Android Ethereum Client</name>
+  <description>Android port of the go-ethereum libraries and node</description>
+  <url>https://github.com/ethereum/go-ethereum</url>
+  <inceptionYear>2015</inceptionYear>
+
+  <licenses>
+    <license>
+      <name>GNU Lesser General Public License, Version 3.0</name>
+      <url>https://www.gnu.org/licenses/lgpl-3.0.en.html</url>
+      <distribution>repo</distribution>
+    </license>
+  </licenses>
+
+  <organization>
+    <name>Ethereum</name>
+    <url>https://ethereum.org</url>
+  </organization>
+
+  <developers>
+    <developer>
+      <id>karalabe</id>
+      <name>Péter Szilágyi</name>
+      <email>peterke@gmail.com</email>
+      <url>https://github.com/karalabe</url>
+      <properties>
+        <picUrl>https://www.gravatar.com/avatar/2ecbf0f5b4b79eebf8c193e5d324357f?s=256</picUrl>
+      </properties>
+    </developer>
+  </developers>
+
+  <contributors>{{range .Contributors}}
+    <contributor>
+      <name>{{.Name}}</name>
+      <email>{{.Email}}</email>
+    </contributor>{{end}}
+  </contributors>
+
+  <issueManagement>
+    <system>GitHub Issues</system>
+    <url>https://github.com/ethereum/go-ethereum/issues/</url>
+  </issueManagement>
+
+  <scm>
+    <url>https://github.com/ethereum/go-ethereum</url>
+  </scm>
+</project>
diff --git a/build/mvn.settings b/build/mvn.settings
new file mode 100644
index 0000000000000000000000000000000000000000..406b409b9b5dce3fca66d2bb4dfc2f78a40c6193
--- /dev/null
+++ b/build/mvn.settings
@@ -0,0 +1,24 @@
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
+                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
+  <servers>
+    <server>
+      <id>ossrh</id>
+      <username>${env.ANDROID_SONATYPE_USERNAME}</username>
+      <password>${env.ANDROID_SONATYPE_PASSWORD}</password>
+    </server>
+  </servers>
+  <profiles>
+    <profile>
+      <id>ossrh</id>
+      <activation>
+        <activeByDefault>true</activeByDefault>
+      </activation>
+      <properties>
+        <gpg.executable>gpg</gpg.executable>
+        <gpg.passphrase></gpg.passphrase>
+      </properties>
+    </profile>
+  </profiles>
+</settings>
diff --git a/internal/build/util.go b/internal/build/util.go
index ce17ce220be03967d3b498a4e9af4fda81850e36..c7e0614f2491093a562b33687eeec491f97d710f 100644
--- a/internal/build/util.go
+++ b/internal/build/util.go
@@ -56,7 +56,7 @@ func GOPATH() string {
 	if len(path) == 0 {
 		log.Fatal("GOPATH is not set")
 	}
-	// Ensure that our internal vendor folder in on GOPATH
+	// Ensure that our internal vendor folder is on GOPATH
 	vendor, _ := filepath.Abs(filepath.Join("build", "_vendor"))
 	for _, dir := range path {
 		if dir == vendor {
diff --git a/mobile/geth.go b/mobile/geth.go
index cb421868cc6b04f84258160929374f860948a0b3..969919ba8c5a7f6802d772349305ecaeaf87e962 100644
--- a/mobile/geth.go
+++ b/mobile/geth.go
@@ -32,7 +32,7 @@ import (
 	"github.com/ethereum/go-ethereum/ethclient"
 	"github.com/ethereum/go-ethereum/node"
 	"github.com/ethereum/go-ethereum/p2p/nat"
-	"github.com/ethereum/go-ethereum/whisper"
+	"github.com/ethereum/go-ethereum/whisper/whisperv2"
 )
 
 // NodeConfig represents the collection of configuration values to fine tune the Geth
@@ -150,7 +150,7 @@ func NewNode(datadir string, config *NodeConfig) (*Node, error) {
 	}
 	// Register the Whisper protocol if requested
 	if config.WhisperEnabled {
-		if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
+		if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisperv2.New(), nil }); err != nil {
 			return nil, fmt.Errorf("whisper init: %v", err)
 		}
 	}