Bug 874132 - Reland Part 3: Replace uses of apkbuilder with zip and custom debug signing tool. r=jmaher,mfinkle
☠☠ backed out by 0bd279c73d1f ☠ ☠
authorNick Alexander <nalexander@mozilla.com>
Wed, 19 Jun 2013 17:05:14 -0700
changeset 135733 833ca9a17792d78e8f57dae5cc8e36d0042bb132
parent 135732 20542fdcbe71adbe0dfd5ea0c5bf69055e4ec5b8
child 135734 661b48de7a7007c86b1b6d0befbc85ef4e1e6d7e
push idunknown
push userunknown
push dateunknown
reviewersjmaher, mfinkle
bugs874132
milestone24.0a1
Bug 874132 - Reland Part 3: Replace uses of apkbuilder with zip and custom debug signing tool. r=jmaher,mfinkle This incorporates follow-ups including: rename the debug key; check if key alias exists rather than just testing for keystore existence; set $(AIDL). Renaming the debug key works around an un-confirmed JDK bug in jarsigner, where '-debug' and 'debug' compare the same.
build/mobile/robocop/Makefile.in
build/mobile/sutagent/android/Makefile.in
build/mobile/sutagent/android/fencp/Makefile.in
build/mobile/sutagent/android/ffxcp/Makefile.in
build/mobile/sutagent/android/watcher/Makefile.in
config/android-common.mk
mobile/android/base/Makefile.in
mobile/android/build.mk
mobile/android/debug_sign_tool.py
testing/testsuite-targets.mk
toolkit/mozapps/installer/packager.mk
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -8,16 +8,18 @@ srcdir      = @srcdir@
 VPATH       = @srcdir@
 
 mobile-tests := mobile/android/base/tests
 TESTPATH     := $(topsrcdir)/$(mobile-tests)
 dir-tests    := $(DEPTH)/$(mobile-tests)
 
 include $(DEPTH)/config/autoconf.mk
 
+ANDROID_APK_NAME := robocop-debug
+
 ROBOTIUM_PATH = $(srcdir)/robotium-solo-3.6.jar
 
 JAVAFILES = \
   R.java \
   $(NULL)
 
 RES_FILES = \
   res/values/strings.xml \
@@ -76,49 +78,55 @@ MOCHITEST_ROBOCOP_FILES := \
   $(wildcard $(TESTPATH)/*.xml) \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml \
   $(java-tests-dep) \
   $(_JAVA_HARNESS) \
   classes.dex \
-  robocop.ap_ \
-  robocop-debug-signed.apk \
-  robocop-debug-signed-unaligned.apk \
+  $(ANDROID_APK_NAME).ap_ \
+  $(ANDROID_APK_NAME)-unsigned-unaligned.apk \
+  $(ANDROID_APK_NAME)-unaligned.apk \
+  $(ANDROID_APK_NAME).apk \
   $(robocop-deps) \
   $(NULL)
 
 DEFINES += \
   -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \
   $(NULL)
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar:$(ROBOTIUM_PATH)
 
 include $(topsrcdir)/config/rules.mk
 
 # Override rules.mk java flags with the android specific ones
 include $(topsrcdir)/config/android-common.mk
 
 GENERATED_DIRS_tools = classes $(dir-tests)
 
-libs:: robocop-debug-signed.apk
+tools:: $(ANDROID_APK_NAME).apk
 
-classes.dex: robocop.ap_
+classes.dex: $(ANDROID_APK_NAME).ap_
 classes.dex: $(robocop-deps)
 classes.dex: $(java-harness-dep)
 classes.dex: $(java-tests-dep)
 	$(JAVAC) $(JAVAC_FLAGS) -d classes $(JAVAFILES) $(_JAVA_HARNESS) $(java-tests-dep)
 	$(DX) --dex --output=$@ classes $(ROBOTIUM_PATH) $(ANDROID_COMPT_LIB)
 
-robocop.ap_: AndroidManifest.xml $(TESTPATH)/assets/*
+$(ANDROID_APK_NAME).ap_: AndroidManifest.xml $(TESTPATH)/assets/*
 	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -I . -S res -A $(TESTPATH)/assets -F $@ -J ./
 
-robocop-debug-signed-unaligned.apk: robocop.ap_ classes.dex
-	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z robocop.ap_ -f classes.dex
+$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex
+	cp $< $@
+	$(ZIP) -0 $@ classes.dex
 
-robocop-debug-signed.apk: robocop-debug-signed-unaligned.apk
-	$(ZIPALIGN) -f -v 4 $^ $@
+$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk
+	cp $< $@
+	$(DEBUG_JARSIGNER) $@
+
+$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk
+	$(ZIPALIGN) -f -v 4 $< $@
 
 # PP_java-tests not fully usable here
 # Intermediate step toward a library rule.
 $(dir-tests)/%.java: $(TESTPATH)/%.java.in $(call mkdir_deps,$(dir-tests))
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
--- a/build/mobile/sutagent/android/Makefile.in
+++ b/build/mobile/sutagent/android/Makefile.in
@@ -4,16 +4,18 @@
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+ANDROID_APK_NAME := sutAgentAndroid
+
 JAVAFILES = \
   AlertLooperThread.java \
   ASMozStub.java \
   CmdWorkerThread.java \
   DataWorkerThread.java \
   DoAlert.java \
   DoCommand.java \
   FindProcThread.java \
@@ -34,45 +36,44 @@ RES_FILES = \
   res/drawable/ic_stat_warning.png \
   res/layout/main.xml \
   res/values/strings.xml \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
-  sutAgentAndroid.apk  \
-  sutAgentAndroid.ap_ \
-  sutAgentAndroid-unsigned-unaligned.apk \
-  sutAgentAndroid-unaligned.apk \
+  $(ANDROID_APK_NAME).ap_ \
+  $(ANDROID_APK_NAME)-unsigned-unaligned.apk \
+  $(ANDROID_APK_NAME)-unaligned.apk \
+  $(ANDROID_APK_NAME).apk \
   $(NULL)
 
 GARBAGE_DIRS += network-libs
 
 EXTRA_JARS = $(srcdir)/network-libs/commons-net-2.0.jar:$(srcdir)/network-libs/jmdns.jar
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar:$(EXTRA_JARS)
 
 include $(topsrcdir)/config/rules.mk
 
 # include Android specific java flags - using these instead of what's in rules.mk
 include $(topsrcdir)/config/android-common.mk
 
-tools:: sutAgentAndroid.apk
+tools:: $(ANDROID_APK_NAME).apk
 
 classes.dex: $(JAVAFILES)
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES))
 	$(DX) --dex --output=$@ classes $(subst :, ,$(EXTRA_JARS))
 
-sutAgentAndroid.ap_: $(srcdir)/AndroidManifest.xml
+$(ANDROID_APK_NAME).ap_: AndroidManifest.xml
 	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@
 
-sutAgentAndroid-unsigned-unaligned.apk: sutAgentAndroid.ap_ classes.dex
-	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z sutAgentAndroid.ap_ -f classes.dex
-
-sutAgentAndroid-unaligned.apk: sutAgentAndroid-unsigned-unaligned.apk
+$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex
 	cp $< $@
-ifdef JARSIGNER
-	$(JARSIGNER) $@
-endif
+	$(ZIP) -0 $@ classes.dex
 
-sutAgentAndroid.apk: sutAgentAndroid-unaligned.apk
-	$(ZIPALIGN) -f -v 4 sutAgentAndroid-unaligned.apk $@
+$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk
+	cp $< $@
+	$(DEBUG_JARSIGNER) $@
+
+$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk
+	$(ZIPALIGN) -f -v 4 $< $@
--- a/build/mobile/sutagent/android/fencp/Makefile.in
+++ b/build/mobile/sutagent/android/fencp/Makefile.in
@@ -4,16 +4,18 @@
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+ANDROID_APK_NAME := FenCP
+
 JAVAFILES = \
   DirCursor.java \
   FenCP.java \
   FenCPFP.java \
   FileCursor.java \
   R.java \
   $(NULL)
 
@@ -23,40 +25,42 @@ RES_FILES = \
   res/drawable-mdpi/icon.png \
   res/layout/main.xml \
   res/values/strings.xml \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
-  FenCP.apk  \
+  $(ANDROID_APK_NAME).ap_ \
+  $(ANDROID_APK_NAME)-unsigned-unaligned.apk \
+  $(ANDROID_APK_NAME)-unaligned.apk \
+  $(ANDROID_APK_NAME).apk \
   $(NULL)
 
 GARBAGE_DIRS += network-libs
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
 include $(topsrcdir)/config/rules.mk
 
 # include Android specific java flags - using these instead of what's in rules.mk
 include $(topsrcdir)/config/android-common.mk
 
-tools:: FenCP.apk
+tools:: $(ANDROID_APK_NAME).apk
 
 classes.dex: $(JAVAFILES)
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES))
 	$(DX) --dex --output=$@ classes
 
-FenCP.ap_: $(srcdir)/AndroidManifest.xml
-	$(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
+$(ANDROID_APK_NAME).ap_: AndroidManifest.xml
+	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@
 
-FenCP-unsigned-unaligned.apk: FenCP.ap_ classes.dex
-	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z FenCP.ap_ -f classes.dex
+$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex
+	cp $< $@
+	$(ZIP) -0 $@ classes.dex
 
-FenCP-unaligned.apk: FenCP-unsigned-unaligned.apk
-	cp  FenCP-unsigned-unaligned.apk $@
-ifdef JARSIGNER
-  $(JARSIGNER) $@
-endif
+$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk
+	cp $< $@
+	$(DEBUG_JARSIGNER) $@
 
-FenCP.apk: FenCP-unaligned.apk
-	$(ZIPALIGN) -f -v 4 FenCP-unaligned.apk $@
+$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk
+	$(ZIPALIGN) -f -v 4 $< $@
--- a/build/mobile/sutagent/android/ffxcp/Makefile.in
+++ b/build/mobile/sutagent/android/ffxcp/Makefile.in
@@ -4,16 +4,18 @@
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+ANDROID_APK_NAME := FfxCP
+
 JAVAFILES = \
   DirCursor.java \
   ffxcp.java \
   FfxCPFP.java \
   FileCursor.java \
   R.java \
   $(NULL)
 
@@ -23,40 +25,42 @@ RES_FILES = \
   res/drawable-mdpi/icon.png \
   res/layout/main.xml \
   res/values/strings.xml \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
-  FfxCP.apk  \
+  $(ANDROID_APK_NAME).ap_ \
+  $(ANDROID_APK_NAME)-unsigned-unaligned.apk \
+  $(ANDROID_APK_NAME)-unaligned.apk \
+  $(ANDROID_APK_NAME).apk \
   $(NULL)
 
 GARBAGE_DIRS += network-libs
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
 include $(topsrcdir)/config/rules.mk
 
 # include Android specific java flags - using these instead of what's in rules.mk
 include $(topsrcdir)/config/android-common.mk
 
-tools:: FfxCP.apk
+tools:: $(ANDROID_APK_NAME).apk
 
 classes.dex: $(JAVAFILES)
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES))
 	$(DX) --dex --output=$@ classes
 
-FfxCP.ap_: $(srcdir)/AndroidManifest.xml
-	$(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
+$(ANDROID_APK_NAME).ap_: AndroidManifest.xml
+	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@
 
-FfxCP-unsigned-unaligned.apk: FfxCP.ap_ classes.dex
-	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z FfxCP.ap_ -f classes.dex
+$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex
+	cp $< $@
+	$(ZIP) -0 $@ classes.dex
 
-FfxCP-unaligned.apk: FfxCP-unsigned-unaligned.apk
-	cp  FfxCP-unsigned-unaligned.apk $@
-ifdef JARSIGNER
-  $(JARSIGNER) $@
-endif
+$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk
+	cp $< $@
+	$(DEBUG_JARSIGNER) $@
 
-FfxCP.apk: FfxCP-unaligned.apk
-	$(ZIPALIGN) -f -v 4 FfxCP-unaligned.apk $@
+$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk
+	$(ZIPALIGN) -f -v 4 $< $@
--- a/build/mobile/sutagent/android/watcher/Makefile.in
+++ b/build/mobile/sutagent/android/watcher/Makefile.in
@@ -4,16 +4,18 @@
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+ANDROID_APK_NAME := Watcher
+
 JAVAFILES = \
   IWatcherService.java \
   RedirOutputThread.java \
   R.java \
   WatcherMain.java \
   WatcherReceiver.java \
   WatcherService.java \
   $(NULL)
@@ -27,46 +29,43 @@ RES_FILES = \
   res/drawable-mdpi/ateamlogo.png \
   res/layout/main.xml \
   res/values/strings.xml \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
-  Watcher.apk  \
+  $(ANDROID_APK_NAME).ap_ \
+  $(ANDROID_APK_NAME)-unsigned-unaligned.apk \
+  $(ANDROID_APK_NAME)-unaligned.apk \
+  $(ANDROID_APK_NAME).apk \
   $(NULL)
 
 GARBAGE_DIRS += res classes network-libs
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
 include $(topsrcdir)/config/rules.mk
 
 # include Android specific java flags - using these instead of what's in rules.mk
 include $(topsrcdir)/config/android-common.mk
 
-tools:: Watcher.apk
+tools:: $(ANDROID_APK_NAME).apk
 
 classes.dex: $(JAVAFILES)
 	$(NSINSTALL) -D classes
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES))
 	$(DX) --dex --output=$@ classes
 
-Watcher.ap_: $(srcdir)/AndroidManifest.xml
-	$(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
+$(ANDROID_APK_NAME).ap_: AndroidManifest.xml
+	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@
 
-Watcher-unsigned-unaligned.apk: Watcher.ap_ classes.dex
-	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z Watcher.ap_ -f classes.dex
+$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex
+	cp $< $@
+	$(ZIP) -0 $@ classes.dex
 
-Watcher-unaligned.apk: Watcher-unsigned-unaligned.apk
-	cp  Watcher-unsigned-unaligned.apk $@
-ifdef JARSIGNER
-  $(JARSIGNER) $@
-endif
+$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk
+	cp $< $@
+	$(DEBUG_JARSIGNER) $@
 
-Watcher.apk: Watcher-unaligned.apk
-	$(ZIPALIGN) -f -v 4 Watcher-unaligned.apk $@
-
-export::
-	$(NSINSTALL) -D res
-	@(cd $(srcdir)/res && tar $(TAR_CREATE_FLAGS) - *) | (cd $(DEPTH)/build/mobile/sutagent/android/watcher/res && tar -xf -)
-
+$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk
+	$(ZIPALIGN) -f -v 4 $< $@
--- a/config/android-common.mk
+++ b/config/android-common.mk
@@ -9,22 +9,21 @@ ifndef ANDROID_SDK
 endif
 
 ifndef JAVA_CLASSPATH
   $(error JAVA_CLASSPATH must be defined before including android-common.mk)
 endif
 
 DX=$(ANDROID_BUILD_TOOLS)/dx
 AAPT=$(ANDROID_BUILD_TOOLS)/aapt
-APKBUILDER=$(ANDROID_SDK)/../../tools/apkbuilder
+AIDL=$(ANDROID_BUILD_TOOLS)/aidl
+ADB=$(ANDROID_PLATFORM_TOOLS)/adb
 ZIPALIGN=$(ANDROID_SDK)/../../tools/zipalign
-
-ifdef JARSIGNER
-  APKBUILDER_FLAGS += -u
-endif
+# DEBUG_JARSIGNER always debug signs.
+DEBUG_JARSIGNER=$(PYTHON) $(call core_abspath,$(topsrcdir)/mobile/android/debug_sign_tool.py)
 
 # For Android, this defaults to $(ANDROID_SDK)/android.jar
 ifndef JAVA_BOOTCLASSPATH
   JAVA_BOOTCLASSPATH = $(ANDROID_SDK)/android.jar:$(ANDROID_COMPAT_LIB)
 endif
 
 # For Android, we default to 1.5
 ifndef JAVA_VERSION
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -1216,17 +1216,17 @@ jars/webrtc.jar: $(addprefix $(srcdir)/,
 	$(JAR) cMf jars/webrtc.jar -C classes/webrtc .
 endif
 
 jars:
 	@echo "MKDIR jars"
 	$(NSINSTALL) -D jars
 
 $(AIDL_AUTOGEN_FILES): %.java: %.aidl
-	$(ANDROID_PLATFORM_TOOLS)/aidl -I$(srcdir)/braille $<
+	$(AIDL) -I$(srcdir)/braille $<
 
 CLASSES_WITH_JNI= \
     org.mozilla.gecko.GeckoAppShell \
     org.mozilla.gecko.GeckoJavaSampler \
     org.mozilla.gecko.gfx.NativePanZoomController \
     org.mozilla.gecko.ANRReporter \
     $(NULL)
 
--- a/mobile/android/build.mk
+++ b/mobile/android/build.mk
@@ -1,48 +1,52 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include  $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
 
-installer: 
+installer:
 	@$(MAKE) -C mobile/android/installer installer
 
 package:
 	@$(MAKE) -C mobile/android/installer
 
 fast-package:
 	@$(MAKE) package MOZ_FAST_PACKAGE=1
 
 ifeq ($(OS_TARGET),Android)
+# $(ADB) is defined in config/android-common.mk, but that file is not
+# in scope when this file is read, so we define it locally.
+ADB=$(ANDROID_PLATFORM_TOOLS)/adb
+
 ifneq ($(MOZ_ANDROID_INSTALL_TARGET),)
 ANDROID_SERIAL = $(MOZ_ANDROID_INSTALL_TARGET)
 endif
 ifneq ($(ANDROID_SERIAL),)
 export ANDROID_SERIAL
 else
 # Determine if there's more than one device connected
-android_devices=$(filter device,$(shell $(ANDROID_PLATFORM_TOOLS)/adb devices))
+android_devices=$(filter device,$(shell $(ADB) devices))
 ifeq ($(android_devices),)
 install::
 	@echo "No devices are connected.  Connect a device or start an emulator."
 	@exit 1
 else
 ifneq ($(android_devices),device)
 install::
 	@echo "Multiple devices are connected. Define ANDROID_SERIAL to specify the install target."
-	$(ANDROID_PLATFORM_TOOLS)/adb devices
+	$(ADB) devices
 	@exit 1
 endif
 endif
 endif
 
 install::
-	$(ANDROID_PLATFORM_TOOLS)/adb install -r $(DIST)/$(PKG_PATH)$(PKG_BASENAME).apk
+	$(ADB) install -r $(DIST)/$(PKG_PATH)$(PKG_BASENAME).apk
 else
 	@echo "Mobile can't be installed directly."
 	@exit 1
 endif
 
 deb: package
 	@$(MAKE) -C mobile/android/installer deb
 
--- a/mobile/android/debug_sign_tool.py
+++ b/mobile/android/debug_sign_tool.py
@@ -31,85 +31,104 @@ log.addHandler(sh)
 
 
 class DebugKeystore:
     """
     A thin abstraction on top of an Android debug key store.
     """
     def __init__(self, keystore):
         self._keystore = os.path.abspath(os.path.expanduser(keystore))
-        self._alias = 'debug'
+        self._alias = 'androiddebugkey'
         self.verbose = False
         self.keytool = 'keytool'
         self.jarsigner = 'jarsigner'
 
     @property
     def keystore(self):
         return self._keystore
 
     @property
     def alias(self):
         return self._alias
 
-    def _ensure_keystore(self):
-        if os.path.exists(self.keystore):
-            if self.verbose:
-                log.debug('Keystore exists at %s' % self.keystore)
+    def _check(self, args):
+        if self.verbose:
+            subprocess.check_call(args)
         else:
-            self.create_keystore()
+            subprocess.check_output(args)
 
-    def create_keystore(self):
+    def keystore_contains_alias(self):
+        args = [ self.keytool,
+                 '-list',
+                 '-keystore', self.keystore,
+                 '-storepass', 'android',
+                 '-alias', self.alias,
+               ]
+        if self.verbose:
+            args.append('-v')
+        contains = True
         try:
-            path = os.path.dirname(self.keystore)
-            os.makedirs(path)
-        except OSError as exception:
-            if exception.errno != errno.EEXIST:
-                raise
+            self._check(args)
+        except subprocess.CalledProcessError as e:
+            contains = False
+        if self.verbose:
+            log.info('Keystore %s %s alias %s' %
+                     (self.keystore,
+                      'contains' if contains else 'does not contain',
+                      self.alias))
+        return contains
 
+    def create_alias_in_keystore(self):
         args = [ self.keytool,
-                 '-genkey',
-                 '-v',
+                 '-genkeypair',
                  '-keystore', self.keystore,
                  '-storepass', 'android',
                  '-alias', self.alias,
                  '-keypass', 'android',
                  '-dname', 'CN=Android Debug,O=Android,C=US',
                  '-keyalg', 'RSA',
                  '-validity', '365',
                ]
-        subprocess.check_call(args)
         if self.verbose:
-            log.info('Created keystore at %s' % self.keystore)
+            args.append('-v')
+        self._check(args)
+        if self.verbose:
+            log.info('Created alias %s in keystore %s' %
+                     (self.alias, self.keystore))
 
     def sign(self, apk):
-        self._ensure_keystore()
+        if not self.keystore_contains_alias():
+            self.create_alias_in_keystore()
 
         args = [ self.jarsigner,
                  '-digestalg', 'SHA1',
                  '-sigalg', 'MD5withRSA',
                  '-keystore', self.keystore,
                  '-storepass', 'android',
                  apk,
                  self.alias,
                ]
-        subprocess.check_call(args)
         if self.verbose:
-            log.info('Signed %s with keystore at %s' % (apk, self.keystore))
+            args.append('-verbose')
+        self._check(args)
+        if self.verbose:
+            log.info('Signed %s with alias %s from keystore %s' %
+                     (apk, self.alias, self.keystore))
 
 
 def parse_args(argv):
     parser = ArgumentParser(description='Sign Android packages using an Android debug keystore.')
     parser.add_argument('apks', nargs='+',
                         metavar='APK',
                         help='Android packages to be signed')
-    parser.add_argument('-q', '--quiet',
+    parser.add_argument('-v', '--verbose',
                         dest='verbose',
-                        default=True,
-                        action='store_false',
-                        help='quiet output')
+                        default=False,
+                        action='store_true',
+                        help='verbose output')
     parser.add_argument('--keytool',
                         metavar='PATH',
                         default='keytool',
                         help='path to Java keytool')
     parser.add_argument('--jarsigner',
                         metavar='PATH',
                         default='jarsigner',
                         help='path to Java jarsigner')
@@ -130,19 +149,20 @@ def main():
 
     keystore = DebugKeystore(args.keystore)
     keystore.verbose = args.verbose
     keystore.keytool = args.keytool
     keystore.jarsigner = args.jarsigner
 
     if args.force:
         try:
-            keystore.create_keystore()
+            keystore.create_alias_in_keystore()
         except subprocess.CalledProcessError as e:
-            log.error('Failed to force-create keystore')
+            log.error('Failed to force-create alias %s in keystore %s' %
+                      (keystore.alias, keystore.keystore))
             log.error(e)
             return 1
 
     for apk in args.apks:
         try:
             keystore.sign(apk)
         except subprocess.CalledProcessError as e:
             log.error('Failed to sign %s', apk)
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -61,17 +61,17 @@ RUN_MOCHITEST_REMOTE = \
     --console-level=INFO --log-file=./$@.log --file-level=INFO $(DM_FLAGS) --dm_trans=$(DM_TRANS) \
     --app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \
     --testing-modules-dir=$(call core_abspath,_tests/modules) \
     $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
 
 RUN_MOCHITEST_ROBOCOP = \
   rm -f ./$@.log && \
   $(PYTHON) _tests/testing/mochitest/runtestsremote.py \
-    --robocop-apk=$(DEPTH)/build/mobile/robocop/robocop-debug-signed.apk \
+    --robocop-apk=$(DEPTH)/build/mobile/robocop/robocop-debug.apk \
     --robocop-ids=$(DEPTH)/mobile/android/base/fennec_ids.txt \
     --robocop-ini=$(DEPTH)/build/mobile/robocop/robocop.ini \
     --console-level=INFO --log-file=./$@.log --file-level=INFO $(DM_FLAGS) --dm_trans=$(DM_TRANS) \
     --app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \
     $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
 
 ifndef NO_FAIL_ON_TEST_ERRORS
 define check_test_error_internal
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -242,20 +242,22 @@ INNER_UNMAKE_PACKAGE = $(error Try using
 endif #Create an RPM file
 
 
 ifeq ($(MOZ_PKG_FORMAT),APK)
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 include $(MOZILLA_DIR)/config/android-common.mk
 
+# DEBUG_JARSIGNER is defined by android-common.mk and always debug
+# signs.  We want to release sign if possible.
 ifdef MOZ_SIGN_CMD
-JARSIGNER := $(MOZ_SIGN_CMD) -f jar
+RELEASE_JARSIGNER := $(MOZ_SIGN_CMD) -f jar
 else
-JARSIGNER ?= echo
+RELEASE_JARSIGNER := $(DEBUG_JARSIGNER)
 endif
 
 DIST_FILES =
 
 # Place the files in the order they are going to be opened by the linker
 DIST_FILES += libmozalloc.so
 ifndef MOZ_FOLD_LIBS
 DIST_FILES += \
@@ -322,20 +324,22 @@ endif
 GECKO_APP_AP_PATH = $(call core_abspath,$(DEPTH)/mobile/android/base)
 
 ifdef ENABLE_TESTS
 INNER_ROBOCOP_PACKAGE=echo
 ifeq ($(MOZ_BUILD_APP),mobile/android)
 UPLOAD_EXTRA_FILES += robocop.apk
 UPLOAD_EXTRA_FILES += fennec_ids.txt
 ROBOCOP_PATH = $(call core_abspath,$(_ABS_DIST)/../build/mobile/robocop)
+# Robocop and Fennec need to be signed with the same key, which means
+# release signing them both.
 INNER_ROBOCOP_PACKAGE= \
   $(NSINSTALL) $(GECKO_APP_AP_PATH)/fennec_ids.txt $(_ABS_DIST) && \
-  cp $(ROBOCOP_PATH)/robocop-debug-signed-unaligned.apk $(_ABS_DIST)/robocop-unaligned.apk && \
-  $(JARSIGNER) $(_ABS_DIST)/robocop-unaligned.apk && \
+  cp $(ROBOCOP_PATH)/robocop-debug-unsigned-unaligned.apk $(_ABS_DIST)/robocop-unaligned.apk && \
+  $(RELEASE_JARSIGNER) $(_ABS_DIST)/robocop-unaligned.apk && \
   $(ZIPALIGN) -f -v 4 $(_ABS_DIST)/robocop-unaligned.apk $(_ABS_DIST)/robocop.apk
 endif
 else
 INNER_ROBOCOP_PACKAGE=echo 'Testing is disabled - No Robocop for you'
 endif
 
 ifdef MOZ_OMX_PLUGIN
 DIST_FILES += libomxplugin.so libomxplugingb.so libomxplugingb235.so libomxpluginhc.so libomxpluginsony.so libomxpluginfroyo.so libomxpluginjb-htc.so
@@ -365,19 +369,20 @@ INNER_MAKE_PACKAGE	= \
     mv libmozglue.so $(MOZ_CHILD_PROCESS_NAME) lib/$(ABI_DIR) && \
     mv $(SO_LIBRARIES) assets && \
     unzip -o $(_ABS_DIST)/gecko.ap_ && \
     rm $(_ABS_DIST)/gecko.ap_ && \
     $(ZIP) -0 $(_ABS_DIST)/gecko.ap_ $(ASSET_SO_LIBRARIES) && \
     $(ZIP) -r9D $(_ABS_DIST)/gecko.ap_ $(DIST_FILES) -x $(NON_DIST_FILES) $(SZIP_LIBRARIES) && \
     $(ZIP) -0 $(_ABS_DIST)/gecko.ap_ $(OMNIJAR_NAME)) && \
   rm -f $(_ABS_DIST)/gecko.apk && \
-  $(APKBUILDER) $(_ABS_DIST)/gecko.apk -v $(APKBUILDER_FLAGS) -z $(_ABS_DIST)/gecko.ap_ -f $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/classes.dex && \
+  cp $(_ABS_DIST)/gecko.ap_ $(_ABS_DIST)/gecko.apk && \
+  $(ZIP) -j0 $(_ABS_DIST)/gecko.apk $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/classes.dex && \
   cp $(_ABS_DIST)/gecko.apk $(_ABS_DIST)/gecko-unsigned-unaligned.apk && \
-  $(JARSIGNER) $(_ABS_DIST)/gecko.apk && \
+  $(RELEASE_JARSIGNER) $(_ABS_DIST)/gecko.apk && \
   $(ZIPALIGN) -f -v 4 $(_ABS_DIST)/gecko.apk $(PACKAGE) && \
   $(INNER_ROBOCOP_PACKAGE)
 
 INNER_UNMAKE_PACKAGE	= \
   mkdir $(MOZ_PKG_DIR) && \
   ( cd $(MOZ_PKG_DIR) && \
     $(UNZIP) $(UNPACKAGE) && \
     mv lib/$(ABI_DIR)/libmozglue.so . && \