Bug 1102488 - Part 1: Factor out constants JAR. r=rnewman
☠☠ backed out by ce46b6f29846 ☠ ☠
authorNick Alexander <nalexander@mozilla.com>
Mon, 24 Nov 2014 15:37:30 -0800
changeset 217272 ae7b2705bfa724a8f1b25b5f49a63a8bcad3df30
parent 217271 e3a52be782f72f22e4ed8485600eb97bb508cd0b
child 217273 51e6a0c2187bd1e725eee5bf5e2bf1033a8f4b97
push id10137
push usernalexander@mozilla.com
push dateTue, 25 Nov 2014 04:31:33 +0000
treeherderfx-team@f264f1d096a1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrnewman
bugs1102488
milestone36.0a1
Bug 1102488 - Part 1: Factor out constants JAR. r=rnewman The constants JAR contains AppConstants and SysInfo. SysInfo depended on HardwareUtils for one function, which can (should?) be in SysInfo anyway, so I moved it. Both SysInfo and AppConstants should be available to Robocop tests, but they are compiled too early to access RobocopTarget. Since nothing in GeckoView should know about GeckoView, I moved the annotation to the Fennec-level proguard.cfg.
mobile/android/base/AppConstants.java.in
mobile/android/base/Makefile.in
mobile/android/base/SysInfo.java.in
mobile/android/base/geckoview.ddf
mobile/android/base/moz.build
mobile/android/base/util/HardwareUtils.java
mobile/android/config/proguard.cfg
--- a/mobile/android/base/AppConstants.java.in
+++ b/mobile/android/base/AppConstants.java.in
@@ -1,30 +1,33 @@
 //#filter substitution
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko;
 
-import org.mozilla.gecko.mozglue.RobocopTarget;
+import android.os.Build;
 
-import android.os.Build;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * A collection of constants that pertain to the build and runtime state of the
  * application. Typically these are sourced from build-time definitions (see
  * Makefile.in). This is a Java-side substitute for nsIXULAppInfo, amongst
  * other things.
  *
  * See also SysInfo.java, which includes some of the values available from
  * nsSystemInfo inside Gecko.
  */
-@RobocopTarget
+// Normally, we'd annotate with @RobocopTarget.  Since AppConstants is compiled
+// before RobocopTarget, we instead add o.m.g.AppConstants directly to the
+// Proguard configuration.
 public class AppConstants {
     public static final String ANDROID_PACKAGE_NAME = "@ANDROID_PACKAGE_NAME@";
     public static final String MANGLED_ANDROID_PACKAGE_NAME = "@MANGLED_ANDROID_PACKAGE_NAME@";
 
     /**
      * Encapsulates access to compile-time version definitions, allowing
      * for dead code removal for particular APKs.
      */
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -74,16 +74,17 @@ ifdef MOZ_NATIVE_DEVICES
         $(ANDROID_MEDIAROUTER_LIB) \
         $(ANDROID_APPCOMPAT_LIB) \
         $(NULL)
 endif
 
 JAVA_CLASSPATH := $(subst $(NULL) ,:,$(strip $(JAVA_CLASSPATH)))
 
 ALL_JARS = \
+  constants.jar \
   gecko-R.jar \
   gecko-browser.jar \
   gecko-mozglue.jar \
   gecko-thirdparty.jar \
   gecko-util.jar \
   sync-thirdparty.jar \
   $(NULL)
 
@@ -146,17 +147,17 @@ classycle_jar := $(topsrcdir)/mobile/and
 		-dependencies=@$< \
 		$(ALL_JARS)
 	@$(TOUCH) $@
 
 # We touch the target file before invoking Proguard so that Proguard's
 # outputs are fresher than the target, preventing a subsequent
 # invocation from thinking Proguard's outputs are stale.  This is safe
 # because Make removes the target file if any recipe command fails.
-.proguard.deps: .geckoview.deps $(ALL_JARS)
+.proguard.deps: .geckoview.deps $(ALL_JARS) $(topsrcdir)/mobile/android/config/proguard.cfg
 	$(REPORT_BUILD)
 	@$(TOUCH) $@
 	java \
 		-Xmx512m -Xms128m \
 		-jar $(ANDROID_SDK_ROOT)/tools/proguard/lib/proguard.jar \
 		@$(topsrcdir)/mobile/android/config/proguard.cfg \
 		-optimizationpasses $(PROGUARD_PASSES) \
 		-injars $(subst ::,:,$(subst $(NULL) ,:,$(strip $(ALL_JARS)))) \
@@ -189,17 +190,17 @@ ANNOTATION_PROCESSOR_JAR_FILES := $(DEPT
 
 GeneratedJNIWrappers.cpp: $(ANNOTATION_PROCESSOR_JAR_FILES)
 GeneratedJNIWrappers.cpp: $(ALL_JARS)
 	$(JAVA) -classpath gecko-mozglue.jar:$(JAVA_BOOTCLASSPATH):$(JAVA_CLASSPATH):$(ANNOTATION_PROCESSOR_JAR_FILES) org.mozilla.gecko.annotationProcessors.AnnotationProcessor $(ALL_JARS)
 
 # These _PP_JAVAFILES are specified in moz.build and defined in
 # backend.mk, which is included by config.mk.  Therefore this needs to
 # be defined after config.mk is included.
-PP_JAVAFILES := $(filter-out generated/org/mozilla/gecko/R.java,$(gecko-mozglue_PP_JAVAFILES) $(gecko-browser_PP_JAVAFILES))
+PP_JAVAFILES := $(filter-out generated/org/mozilla/gecko/R.java,$(gecko-mozglue_PP_JAVAFILES) $(gecko-browser_PP_JAVAFILES) $(constants_PP_JAVAFILES))
 
 manifest := \
   AndroidManifest.xml.in \
   WebappManifestFragment.xml.frag.in \
   $(NULL)
 
 PP_TARGETS += manifest
 
--- a/mobile/android/base/SysInfo.java.in
+++ b/mobile/android/base/SysInfo.java.in
@@ -1,52 +1,64 @@
 //#filter substitution
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko;
 
-import org.mozilla.gecko.util.HardwareUtils;
-
 import android.os.StrictMode;
 import android.util.Log;
 
 import java.io.File;
 import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 import java.util.regex.Pattern;
 
 /**
  * A collection of system info values, broadly mirroring a subset of
  * nsSystemInfo. See also the constants in AppConstants, which reflect
  * much of nsIXULAppInfo.
  */
+// Normally, we'd annotate with @RobocopTarget.  Since SysInfo is compiled
+// before RobocopTarget, we instead add o.m.g.SysInfo directly to the Proguard
+// configuration.
 public final class SysInfo {
     private static final String LOG_TAG = "GeckoSysInfo";
 
+    // Number of bytes of /proc/meminfo to read in one go.
+    private static final int MEMINFO_BUFFER_SIZE_BYTES = 256;
+
     // We don't mind an instant of possible duplicate work, we only wish to
     // avoid inconsistency, so we don't bother with synchronization for
     // these.
     private static volatile int cpuCount = -1;
 
-      /**
-       * Get the number of cores on the device.
-       *
-       * We can't use a nice tidy API call, because they're all wrong:
-       *
-       * <http://stackoverflow.com/questions/7962155/how-can-you-detect-a-dual-core-
-       * cpu-on-an-android-device-from-code>
-       *
-       * This method is based on that code.
-       *
-       * @return the number of CPU cores, or 1 if the number could not be
-       *         determined.
-       */
+    private static volatile int totalRAM = -1;
+
+    /**
+     * Get the number of cores on the device.
+     *
+     * We can't use a nice tidy API call, because they're all wrong:
+     *
+     * <http://stackoverflow.com/questions/7962155/how-can-you-detect-a-dual-core-
+     * cpu-on-an-android-device-from-code>
+     *
+     * This method is based on that code.
+     *
+     * @return the number of CPU cores, or 1 if the number could not be
+     *         determined.
+     */
     public static int getCPUCount() {
         if (cpuCount > 0) {
             return cpuCount;
         }
 
         // Avoid a strict mode warning.
         StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
         try {
@@ -68,20 +80,103 @@ public final class SysInfo {
             return cpuCount = dir.listFiles(new CpuFilter()).length;
         } catch (Exception e) {
             Log.w(LOG_TAG, "Assuming 1 CPU; got exception.", e);
             return cpuCount = 1;
         }
     }
 
     /**
-     * Wraps HardwareUtils so callers don't need to know about it.
+     * Helper functions used to extract key/value data from /proc/meminfo
+     * Pulled from:
+     * http://androidxref.com/4.2_r1/xref/frameworks/base/core/java/com/android/internal/util/MemInfoReader.java
+     */
+    private static boolean matchMemText(byte[] buffer, int index, int bufferLength, byte[] text) {
+        final int N = text.length;
+        if ((index + N) >= bufferLength) {
+            return false;
+        }
+        for (int i = 0; i < N; i++) {
+            if (buffer[index + i] != text[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Parses a line like:
+     *
+     *  MemTotal: 1605324 kB
+     *
+     * into 1605324.
+     *
+     * @return the first uninterrupted sequence of digits following the
+     *         specified index, parsed as an integer value in KB.
+     */
+    private static int extractMemValue(byte[] buffer, int offset, int length) {
+        if (offset >= length) {
+            return 0;
+        }
+
+        while (offset < length && buffer[offset] != '\n') {
+            if (buffer[offset] >= '0' && buffer[offset] <= '9') {
+                int start = offset++;
+                while (offset < length &&
+                       buffer[offset] >= '0' &&
+                       buffer[offset] <= '9') {
+                    ++offset;
+                }
+                return Integer.parseInt(new String(buffer, start, offset - start), 10);
+            }
+            ++offset;
+        }
+        return 0;
+    }
+
+    /**
+     * Fetch the total memory of the device in MB by parsing /proc/meminfo.
+     *
+     * Of course, Android doesn't have a neat and tidy way to find total
+     * RAM, so we do it by parsing /proc/meminfo.
+     *
+     * @return 0 if a problem occurred, or memory size in MB.
      */
     public static int getMemSize() {
-        return HardwareUtils.getMemSize();
+        if (totalRAM >= 0) {
+            return totalRAM;
+        }
+
+        // This is the string "MemTotal" that we're searching for in the buffer.
+        final byte[] MEMTOTAL = {'M', 'e', 'm', 'T', 'o', 't', 'a', 'l'};
+        try {
+            final byte[] buffer = new byte[MEMINFO_BUFFER_SIZE_BYTES];
+            final FileInputStream is = new FileInputStream("/proc/meminfo");
+            try {
+                final int length = is.read(buffer);
+
+                for (int i = 0; i < length; i++) {
+                    if (matchMemText(buffer, i, length, MEMTOTAL)) {
+                        i += 8;
+                        totalRAM = extractMemValue(buffer, i, length) / 1024;
+                        Log.d(LOG_TAG, "System memory: " + totalRAM + "MB.");
+                        return totalRAM;
+                    }
+                }
+            } finally {
+                is.close();
+            }
+
+            Log.w(LOG_TAG, "Did not find MemTotal line in /proc/meminfo.");
+            return totalRAM = 0;
+        } catch (FileNotFoundException f) {
+            return totalRAM = 0;
+        } catch (IOException e) {
+            return totalRAM = 0;
+        }
     }
 
     /**
      * @return the SDK version supported by this device, such as '16'.
      */
     public static int getVersion() {
         return android.os.Build.VERSION.SDK_INT;
     }
--- a/mobile/android/base/geckoview.ddf
+++ b/mobile/android/base/geckoview.ddf
@@ -35,16 +35,17 @@ show allResults
   org.mozilla.gecko.GlobalHistory \
   org.mozilla.gecko.InputMethods \
   org.mozilla.gecko.NSSBridge \
   org.mozilla.gecko.NotificationClient \
   org.mozilla.gecko.NotificationHandler \
   org.mozilla.gecko.PrefsHelper \
   org.mozilla.gecko.SmsManager \
   org.mozilla.gecko.SurfaceBits \
+  org.mozilla.gecko.SysInfo \
   org.mozilla.gecko.TouchEventInterceptor \
   org.mozilla.gecko.ZoomConstraints
 
 [middle] = \
   org.mozilla.gecko.prompts.* \
   org.mozilla.gecko.AlertNotification \
   org.mozilla.gecko.FormAssistPopup \
   org.mozilla.gecko.GeckoActivity \
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -6,16 +6,23 @@
 
 DIRS += ['locales']
 SPHINX_TREES['fennec'] = 'docs'
 
 include('android-services.mozbuild')
 
 thirdparty_source_dir = TOPSRCDIR + '/mobile/android/thirdparty/'
 
+constants_jar = add_java_jar('constants')
+constants_jar.sources = []
+constants_jar.generated_sources = [
+    'org/mozilla/gecko/AppConstants.java',
+    'org/mozilla/gecko/SysInfo.java',
+]
+
 resjar = add_java_jar('gecko-R')
 resjar.sources = []
 resjar.generated_sources += [
     'org/mozilla/gecko/R.java',
 ]
 
 if CONFIG['MOZ_NATIVE_DEVICES']:
     resjar.generated_sources += ['com/google/android/gms/R.java']
@@ -34,19 +41,21 @@ mgjar.sources += [
     'mozglue/generatorannotations/WrapEntireClassForJNI.java',
     'mozglue/JNITarget.java',
     'mozglue/NativeReference.java',
     'mozglue/NativeZip.java',
     'mozglue/RobocopTarget.java',
     'mozglue/WebRTCJNITarget.java',
 ]
 mgjar.generated_sources += [
-    'org/mozilla/gecko/AppConstants.java',
     'org/mozilla/gecko/mozglue/GeckoLoader.java',
 ]
+mgjar.extra_jars += [
+    'constants.jar',
+]
 mgjar.javac_flags += ['-Xlint:all']
 
 gujar = add_java_jar('gecko-util')
 gujar.sources += [
     'util/ActivityResultHandler.java',
     'util/ActivityResultHandlerMap.java',
     'util/ActivityUtils.java',
     'util/Clipboard.java',
@@ -72,17 +81,18 @@ gujar.sources += [
     'util/ProxySelector.java',
     'util/RawResource.java',
     'util/StringUtils.java',
     'util/ThreadUtils.java',
     'util/UIAsyncTask.java',
     'util/WebActivityMapper.java',
 ]
 gujar.extra_jars = [
-    'gecko-mozglue.jar'
+    'constants.jar',
+    'gecko-mozglue.jar',
 ]
 gujar.javac_flags += ['-Xlint:all,-deprecation']
 
 stjar = add_java_jar('sync-thirdparty')
 stjar.sources += [ thirdparty_source_dir + f for f in sync_thirdparty_java_files ]
 stjar.javac_flags = ['-Xlint:none']
 
 if CONFIG['MOZ_WEBRTC']:
@@ -494,18 +504,19 @@ gbjar.sources += [
     'widget/ThemedView.java',
 ]
 gbjar.sources += [ thirdparty_source_dir + f for f in [
     'com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java',
     'com/googlecode/eyesfree/braille/selfbraille/SelfBrailleClient.java',
     'com/googlecode/eyesfree/braille/selfbraille/WriteData.java',
 ] ]
 android_package_dir = CONFIG['ANDROID_PACKAGE_NAME'].replace('.', '/')
-gbjar.generated_sources += [
-    'org/mozilla/gecko/SysInfo.java',
+gbjar.generated_sources = [] # Keep it this way.
+gbjar.extra_jars += [
+    'constants.jar'
 ]
 if CONFIG['MOZ_CRASHREPORTER']:
     gbjar.sources += [ 'CrashReporter.java' ]
     ANDROID_RES_DIRS += [ SRCDIR + '/crashreporter/res' ]
 
 if CONFIG['MOZ_ANDROID_SHARE_OVERLAY']:
     gbjar.sources += [
         'overlays/OverlayConstants.java',
@@ -535,17 +546,17 @@ if CONFIG['MOZ_ANDROID_NEW_TABLET_UI'] a
         'tabs/TabStripAdapter.java',
         'tabs/TabStripItemView.java',
         'tabs/TabStripView.java'
     ]
     ANDROID_RES_DIRS += [ SRCDIR + '/newtablet/res' ]
 
 gbjar.sources += sync_java_files
 gbjar.generated_sources += sync_generated_java_files
-gbjar.extra_jars = [
+gbjar.extra_jars += [
     'gecko-R.jar',
     'gecko-mozglue.jar',
     'gecko-thirdparty.jar',
     'gecko-util.jar',
     'sync-thirdparty.jar',
 ]
 
 moz_native_devices_jars = [
@@ -681,16 +692,17 @@ if CONFIG['MOZ_ANDROID_SEARCH_ACTIVITY']
     # that depends on the Geckoview jars.
     search_source_dir = SRCDIR + '/../search'
     include('../search/search_activity_sources.mozbuild')
 
     search_activity = add_java_jar('search-activity')
     search_activity.sources += [search_source_dir + '/' + f for f in search_activity_sources]
     search_activity.javac_flags += ['-Xlint:all']
     search_activity.extra_jars = [
+        'constants.jar',
         'gecko-R.jar',
         'gecko-browser.jar',
         'gecko-mozglue.jar',
         'gecko-thirdparty.jar',
         'gecko-util.jar'
     ]
 
 generated_recursive_make_targets = ['.aapt.deps', '.locales.deps'] # Captures dependencies on Android manifest and all resources.
--- a/mobile/android/base/util/HardwareUtils.java
+++ b/mobile/android/base/util/HardwareUtils.java
@@ -1,18 +1,16 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.util;
 
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
+import org.mozilla.gecko.SysInfo;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.os.Build;
 import android.util.Log;
 import android.view.ViewConfiguration;
 
@@ -22,24 +20,20 @@ public final class HardwareUtils {
     // Minimum memory threshold for a device to be considered
     // a low memory platform (see isLowMemoryPlatform). This value
     // has be in sync with Gecko's equivalent threshold (defined in
     // xpcom/base/nsMemoryImpl.cpp) and should only be used in cases
     // where we can't depend on Gecko to be up and running e.g. show/hide
     // reading list capabilities in HomePager.
     private static final int LOW_MEMORY_THRESHOLD_MB = 384;
 
-    // Number of bytes of /proc/meminfo to read in one go.
-    private static final int MEMINFO_BUFFER_SIZE_BYTES = 256;
-
     private static final boolean IS_AMAZON_DEVICE = Build.MANUFACTURER.equalsIgnoreCase("Amazon");
     public static final boolean IS_KINDLE_DEVICE = IS_AMAZON_DEVICE &&
                                                    (Build.MODEL.equals("Kindle Fire") ||
                                                     Build.MODEL.startsWith("KF"));
-    private static volatile int sTotalRAM = -1;
 
     private static volatile boolean sInited;
 
     // These are all set once, during init.
     private static volatile boolean sIsLargeTablet;
     private static volatile boolean sIsSmallTablet;
     private static volatile boolean sIsTelevision;
     private static volatile boolean sHasMenuButton;
@@ -94,105 +88,18 @@ public final class HardwareUtils {
     public static boolean isTelevision() {
         return sIsTelevision;
     }
 
     public static boolean hasMenuButton() {
         return sHasMenuButton;
     }
 
-    /**
-    * Helper functions used to extract key/value data from /proc/meminfo
-    * Pulled from:
-    * http://androidxref.com/4.2_r1/xref/frameworks/base/core/java/com/android/internal/util/MemInfoReader.java
-    */
-
-    private static boolean matchMemText(byte[] buffer, int index, int bufferLength, byte[] text) {
-        final int N = text.length;
-        if ((index + N) >= bufferLength) {
-            return false;
-        }
-        for (int i = 0; i < N; i++) {
-            if (buffer[index + i] != text[i]) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Parses a line like:
-     *
-     *  MemTotal: 1605324 kB
-     *
-     * into 1605324.
-     *
-     * @return the first uninterrupted sequence of digits following the
-     *         specified index, parsed as an integer value in KB.
-     */
-    private static int extractMemValue(byte[] buffer, int offset, int length) {
-        if (offset >= length) {
-            return 0;
-        }
-
-        while (offset < length && buffer[offset] != '\n') {
-            if (buffer[offset] >= '0' && buffer[offset] <= '9') {
-                int start = offset++;
-                while (offset < length &&
-                       buffer[offset] >= '0' &&
-                       buffer[offset] <= '9') {
-                    ++offset;
-                }
-                return Integer.parseInt(new String(buffer, start, offset - start), 10);
-            }
-            ++offset;
-        }
-        return 0;
-    }
-
-    /**
-     * Fetch the total memory of the device in MB by parsing /proc/meminfo.
-     *
-     * Of course, Android doesn't have a neat and tidy way to find total
-     * RAM, so we do it by parsing /proc/meminfo.
-     *
-     * @return 0 if a problem occurred, or memory size in MB.
-     */
     public static int getMemSize() {
-        if (sTotalRAM >= 0) {
-            return sTotalRAM;
-        }
-
-        // This is the string "MemTotal" that we're searching for in the buffer.
-        final byte[] MEMTOTAL = {'M', 'e', 'm', 'T', 'o', 't', 'a', 'l'};
-        try {
-            final byte[] buffer = new byte[MEMINFO_BUFFER_SIZE_BYTES];
-            final FileInputStream is = new FileInputStream("/proc/meminfo");
-            try {
-                final int length = is.read(buffer);
-
-                for (int i = 0; i < length; i++) {
-                    if (matchMemText(buffer, i, length, MEMTOTAL)) {
-                        i += 8;
-                        sTotalRAM = extractMemValue(buffer, i, length) / 1024;
-                        Log.d(LOGTAG, "System memory: " + sTotalRAM + "MB.");
-                        return sTotalRAM;
-                    }
-                }
-            } finally {
-                is.close();
-            }
-
-            Log.w(LOGTAG, "Did not find MemTotal line in /proc/meminfo.");
-            return sTotalRAM = 0;
-        } catch (FileNotFoundException f) {
-            return sTotalRAM = 0;
-        } catch (IOException e) {
-            return sTotalRAM = 0;
-        }
+        return SysInfo.getMemSize();
     }
 
     public static boolean isLowMemoryPlatform() {
         final int memSize = getMemSize();
 
         // Fallback to false if we fail to read meminfo
         // for some reason.
         if (memSize == 0) {
--- a/mobile/android/config/proguard.cfg
+++ b/mobile/android/config/proguard.cfg
@@ -189,13 +189,21 @@
 -keep @interface org.mozilla.gecko.mozglue.generatorannotations.WrapEntireClassForJNI
 -keep @org.mozilla.gecko.mozglue.generatorannotations.WrapEntireClassForJNI class *
 -keepclassmembers @org.mozilla.gecko.mozglue.generatorannotations.WrapEntireClassForJNI class * {
     *;
 }
 
 -keep class **.R$*
 
+# Keep classes, and all their contents, compiled before mozglue.RobocopTarget.
+-keep class org.mozilla.gecko.AppConstants {
+    *;
+}
+-keep class org.mozilla.gecko.SysInfo {
+    *;
+}
+
 # Disable obfuscation because it makes exception stack traces more difficult to read.
 -dontobfuscate
 
 # Suppress warnings about missing descriptor classes.
 #-dontnote **,!ch.boye.**,!org.mozilla.gecko.sync.**