Mobile/Distribution Files

< Mobile

Like desktop, mobile Firefox supplies a way for partners to repackage a set of customization files into a Firefox build without recompiling. We don't want to make custom binary builds that need to be QA'd extensively. The "distribution files" concept allows a small set of external files to be used to customize a standard Firefox build. Things that can be customized include:

  • Default bookmarks
  • Preferences
  • Lightweight themes
  • Search engines
  • Add-ons
  • Quickshare defaults
  • Suggested sites

A sample distribution directory is available on github.

Contents

Overview

There are currently three ways distribution files can be ... distributed:

  1. APK distributions: A folder of distribution files is repackaged into the Android APK. During the first run, those files are extracted from the APK and stored in the /data/data/org.mozilla.firefox/distribution/* directory.
  2. System distributions: A folder of distribution files are shipped with the ROM image in /system/org.mozilla.firefox/distribution/* directory. Shipping in this way ensures the files are not easily overwritten or removed by an application update. Note, files found in the /data/data/org.mozilla.firefox/distribution/* directory will "win" in cases where both locations are used.
  3. Over-the-air distributions: A distribution zip file is downloaded over-the-air as a result of a post-install referrer intent. See bug 1013024. This feature should ship with Firefox 33.

Distributions are processed asynchronously during first run. Frontend code can chain post-distribution tasks via Distribution.addOnDistributionReadyCallback.

Each type of data has its own nuances:

  • Default bookmarks are read from the APK directly or from the "distribution" folder (if it already exists) any time a profile is created. These bookmarks are created prior to Firefox's default bookmarks.
  • Preferences are set as "default" preferences on each run, since a "default" preference only lasts for the lifetime of the session. They are created as "default" preferences to allow "user" preferences to override them.
  • Lightweight themes are actually just preferences. The data for the theme must live online somewhere. Typical places are the AMO theme or getpersonas.com sites.
  • Search engine plugins and add-ons must follow a fixed folder layout.
  • If distribution data is processed after the initial quickshare set is loaded, these options won't take effect until next run (bug 1021176).

Bookmarks

Default bookmarks are specified in /distribution/bookmarks.json. This file should contain an array of bookmark JSON objects with the following properties:

  • title: (required) string for the bookmark title
  • url: (required) string for the bookmark URL
  • title.<locale>: (optional) title string for the given locale
  • url.<locale>: (optional) URL string for the given locale
  • icon: (optional) data URI for the bookmark favicon
  • pinned: (optional) boolean for whether or not to pin this bookmark to the about:home top sites
    • NOTE: If a bookmark is pinned, it is not visible in the Bookmarks list. Pinning only affects Tops Sites.

Here is an example bookmarks.json:

[
  {
    "title": "Queso",
    "url": "http://queso.com/",
    "title.en_US": "Cheese",
    "url.en_US": "http://cheese.com",
    "icon": "data:image/png;base64,..."
  }
]

Preferences

Preferences are specified in /distribution/preferences.json. This file should contain a JSON object with the following properties:

  • Global
    • id: (required) a short string unique to this distribution
    • version: (required) version of the distribution (not the version of Firefox)
    • about: (required) a short descriptive (ui-visible) string for this distribution
    • about.<locale>: (optional) descriptive string for the given locale
  • Preferences (optional)
    • This sets default Gecko preferences for the profile (i.e. prefs that would appear in about:config)
  • LocalizablePreferences (optional)
  • LocalizablePreferences.<locale> (optional)
  • ApplicationPreferences (optional)
    • This sets as the app-scoped SharedPreferences for the entire application. Since GeckoPreferenceFragment only accept application-scoped sharedPreferences, this object will let partner also have the ability to override them.
  • AndroidPreferences (optional)
    • This sets Android SharedPreferences for the profile

Here is an example preferences.json:

{
  "Global": {
    "id": "testpartner",
    "version": 1.0,
    "about": "Afiliado de Prueba",
    "about.en-US": "Test Partner"
  },
  "Preferences": {
    "distribution.test.string": "a string pref",
    "distribution.test.boolean": true,
    "distribution.test.int": 5
  },
  "LocalizablePreferences": {
    "distribution.test.localizeable": "http://test.org/%LOCALE%/%LOCALE%/",
    "distribution.test.localizeable-override": "http://test.org/%LOCALE%/%LOCALE%/"
  },
  "LocalizablePreferences.en-US": {
    "distribution.test.localizeable-override": "http://cheese.com"
  },
  "ApplicationPreferences": {
    "newtab.load_homepage": "true"
  },
  "AndroidPreferences": {
     "homepage": "http://www.mozilla.com"
   }
}

Lightweight Themes

Lightweight themes are specified in the "Preferences" section of /distribution/preferences.json. Here is an example of the lightweight theme preferences needed for this to work:

"Preferences": {
  "lightweightThemes.isThemeSelected": true,
  "lightweightThemes.persisted.footerURL": false,
  "lightweightThemes.persisted.headerURL": false,
  "lightweightThemes.usedThemes": "[{\"id\":\"491717\",\"name\":\"colored trees\",\"headerURL\":\"http://getpersonas-cdn.mozilla.net/static/1/7/491717/trees.png?1358076678\",\"footerURL\":\"http://getpersonas-cdn.mozilla.net/static/1/7/491717/trees-base.png?1358076678\",\"textcolor\":\"#1d022b\",\"accentcolor\":\"#000000\",\"iconURL\":\"http://getpersonas-cdn.mozilla.net/static/1/7/491717/preview_small.jpg?1358076678\",\"previewURL\":\"http://getpersonas-cdn.mozilla.net/static/1/7/491717/preview.jpg?1358076678\",\"author\":\"birdyann\",\"description\":\"trees arbres colored colors couleurs\",\"updateURL\":\"https://www.getpersonas.com/en-US/update_check/491717\",\"version\":\"1358076678\",\"updateDate\":1359579038207,\"installDate\":1359579038207}]"
}

Search Plugins

Include search plugins in a directory structure like the following, and they will be automatically loaded:

 * distribution/
   * searchplugins/
     * common/
       * <plugin.xml>
       * <plugin.xml>
       * ...
     * locale/
       * <locale>/
         * <plugin.xml>
         * <plugin.xml>
         * ...
       * <locale>/
         * <plugin.xml>
         * <plugin.xml>
         * ...

If a directory for the locale the browser is running in doesn't exist, Firefox will automatically try the 'default' locale, as defined by the distribution.searchplugins.defaultLocale preference.

To set a given search engine as the default, you need to use LocalizablePreferences to override the browser.search.defaultenginename preference. You should also override the browser.search.order.1 preference to put the default engine in the right sort order. For example:

 {
   ...
   "LocalizablePreferences": {
     "browser.search.defaultenginename": "<name-of-engine>",
     "browser.search.order.1": "<name-of-engine>"
     ...
   }
   ...
 }

Add-ons

Include add-ons in an extensions directory as follows, and they will be automatically loaded:

 * distribution/
   * extensions/
     * someaddon@somedomain.com.xpi
     * ...

Be sure to name the add-on XPI with the ID that's specified in the add-on's install.rdf.

Note: As of Firefox 41, we require all add-ons to be signed through AMO, so you should make sure this XPI is signed properly before shipping in a distribution.

Quickshare defaults

Add a file named history.xml under distribution/quickshare. Its contents should be something like:

<historical-records>
  <historical-record activity="com.android.mms/com.android.mms.ui.ConversationComposer"
                     time="0"
                     weight="1"/>
</historical-records>

Suggested sites

Added by bug 1012462 in Firefox 33.

Use a directory tree like the following. Locales are named via a locale code, such as en-US.

 * distribution/
   * suggestedsites/
     * locales/
       * <locale>/
         * suggestedsites.json
     * res/
       * hdpi/
         * <distro>.png
       * xxhdpi/
         * <distro>.png

The suggestedsites.json file should be a JSON array:

  [
    {
      url: "<site-url>",
      title: "<site-title>",
      imageurl: "gecko.distribution://<image-dir>/<image-name>",
      bgcolor: "<color-hex-code>", /* "#FF00FF" with leading '#' */
      trackingid: <tiles-id> /* optional integer */
    },
    ...
  ]

Where <image-dir> is the directory containing the density-specific image directories (mdpi, hdpi, etc) and <image-name> is the filename under the density-specific directories without the file extension. For example, gecko.distribution://suggestedsites/res/distroimage image URL will be translated to <distribution-root-dir>/suggestedsites/res/xhdpi/distroimage.png on a XHDPI device.

Ideally, the title of your suggested site should be short so that it only displays on one line and does not cover too much of the suggested site icon. That being said, there is a maximum of 2 lines for distribution suggested site titles. Multiple suggested sites should not begin with the same text.

The list of suggested sites from the distribution will be prepended to the default list of suggested sites that ships with Firefox.

Image Specs

When creating images for use in Suggested Sites, please use the following guidelines. Make sure you provide images for all the DPI levels or the thumbnail may not look good on some devices:

Images Sizes (Bounds) - Firefox 57 and above

  • xxhdpi: 270x270
  • xhdpi: 180x180
  • hdpi: 135x135

Images Sizes (Bounds) - Firefox 56 and below

  • xxhdpi: 375x195
  • xhdpi: 250x130
  • hdpi: 188x98
  • mdpi:125x65

Testing a distribution locally

Note: you need a clean install. If you have already run Firefox, go to settings and clear the app's data

APK distribution

  • To create a repackaged distribution:
    • For Firefox for Android 45 and later:
      • Your distribution folder contents should be at:
        /path/to/fennec-distribution-sample/assets/distribution
      • Add a line like the following to your mozconfig:
        ac_add_options --with-android-distribution-directory=/path/to/fennec-distribution-sample
      • Make sure you have run ./mach configure before building and packaging
      • See bug 1163082 for details.
      • Note: By default the distribution is going into the install bouncer APK. To change that set MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER to False in mobile/android/moz.configure (See bug 1258372 for details) or add export MOZ_ANDROID_PACKAGE_INSTALL_BOUNCER= to mozconfig in Firefox 50 or later.
    • For Firefox for Android 44 and earlier:
      • Put your distribution folder at objdir/dist/bin/distribution
      • Run ./mach package to package the app
    • When the app is launched, the distribution files will be copied out to /data/data/org.mozilla.fennec_yourname/distribution, and they will override a ROM image distribution if it is present

System distribution

  • To create a ROM image distribution, follow these steps with a rooted phone:
    • Copy the distribution files to a writable location
 adb shell mkdir /data/local/tmp/distribution
 adb push distribution /data/local/tmp/distribution
    • Run adb shell to get and adb shell
 $ su
 # mount
 # mount -o rw,remount /system
 # cp -r /data/local/tmp/distribution /system/<package>/  # (replace <package> with the ID for the package you want to test, e.g. org.mozilla.firefox or org.mozilla.fennec)

Testing distribution download

A distribution download is triggered when two things are true:

  • This is the first run of Firefox (or Clear Data has been used).
  • An INSTALL_REFERRER intent has been received prior to launch.

The Google Play app takes care of sending that intent. You can also manually send it via adb shell. Assuming you're testing Nightly:

am broadcast -a com.android.vending.INSTALL_REFERRER \
             -n org.mozilla.firefox/org.mozilla.gecko.distribution.ReferrerReceiver \
             -f 32 \
             --es "referrer" "utm_source=mozilla\&utm_content=testsigned\&utm_campaign=distribution"

This will fetch the "testsigned.jar" distribution.

(The -f 32 flag makes sure this intent is sent to stopped packages.)

You can also do this from another Android application:

        Intent i = new Intent("com.android.vending.INSTALL_REFERRER");
        i.setPackage("org.mozilla.firefox");
        i.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
        i.putExtra("referrer", "utm_source=mozilla&utm_content=testsigned&utm_campaign=distribution");
        getContext().sendBroadcast(i);

You can the below example link to install the testsigned example package:

https://play.google.com/store/apps/details?id=org.mozilla.firefox&referrer=utm_source%3Dmozilla%26utm_content%3Dtestsigned%26utm_campaign%3Ddistribution

This must be on a device that does not have Firefox installed.

Telemetry will be recorded for the download: FENNEC_DISTRIBUTION_DOWNLOAD_TIME_MS records the download time in milliseconds, and FENNEC_DISTRIBUTION_CODE_CATEGORY records the result. The value will be one of these:

0
Unexpected HTTP status.
1
HTTP 1xx. Should not occur.
2
HTTP 2xx. The expected result.
3
HTTP 3xx. Should not occur.
4
HTTP 4xx. Should not occur.
5
HTTP 5xx. Should not occur.
6
The device was offline.
7
Generic fetch exception.
8
Failure during processing of downloaded zip.
9
Security exception: malformed, corrupted, or modified signatures.
10
Malformed distribution file.
11
Fetch: socket error.
12
Fetch: SSL error.
13
Fetch: non-success HTTP response.
14
Fetch: invalid Content-Type header received.

Crafting a distribution referrer

These are the fields exposed in a referrer URL to Play:

utm_content
The distribution name. Must be unique. Identifies the distribution name.
utm_campaign
The campaign name. Must be distribution.
utm_source
Original referrer. Must be mozilla for both downloadable distributions and campaign tracking.
utm_medium
Campaign medium. E.g., email. Not used by Firefox.
utm_term
Search terms. E.g., web+browser. Not used by Firefox.

Distribution bundles

Firefox for Android 46.0 introduced support for distribution bundles based on the MCC (mobile country code, [1]) and MNC (mobile network code) codes of the inserted SIM card. Instead of the default distribution directory Firefox will search a distribution in the following sub directories first:

  • (distribution directory)/<MCC>/<MNC> - For bundled distributions for specific network providers
  • (distribution directory)/<MCC> - For bundled distributions for specific countries
  • (distribution directory)/default - For bundled distributions with no matching MCC/MNC

Example distribution bundle

 distribution
 ├── 208                           // 208 = Country code of France
 │   └── bookmarks.json            // Bookmarks just for users with a SIM card of a French operator
 ├── 262                           // 262 = Country code of Germany
 │   └── 01                        // 01 = Network code of "Deutsche Telekom"
 │       ├── bookmarks.json        // Bookmarks..
 │       └── preferences.json      // ... and preferences just for users of a SIM card of "Deutsche Telekom"
 └── default                       // Default folder
     └── preferences.json          // Preferences for all users with different SIM cards.

Partner Content Providers

Android system images can ship with two content providers to customize installed browsers:

  • content://com.android.partnerbookmarks - For partner bookmarks
  • content://com.android.partnerbrowsercustomizations - For customizations (Details see below)

Firefox for Android supports reading data from both of those content providers (Starting with Firefox for Android 50.0) if this has been enabled by a distribution. If the following preferences are not written by a distribution then Firefox for Android will ignore the data from the partner content providers.

/distribution/preferences.json:

[..]
  "AndroidPreferences": {
    "distribution.read_partner_bookmarks_provider": true,
    "distribution.read_partner_customizations_provider": true
  }
[..]

Bookmarks Content Provider

Firefox for Android (>= 50.0) supports reading bookmarks and folders from the content provider. Reading icons from the content provider is currently not supported (See bug 1286203). Editing and deleting bookmarks/folders is not supported yet but planned.

An example implementation of the content provider can be found in the official Android source tree:

Customizations Content Provider

Firefox for Android (>= 50.0) only supports reading the 'homepage' from the content provider. All other values are ignored.

content://com.android.partnerbrowsercustomizations

Path Column Supported
/homepage homepage Yes
/disableincognitomode disableincognitomode No
/disablebookmarksediting disablebookmarksediting No

Testing

There's a test app with sample data available on GitHub:

The test app uses slightly different content provider authorities to allow being installed on devices that already ship with partner content providers:

  • com.android.partnerbookmarks.test
  • com.android.partnerbrowsercustomizations.test

A custom Firefox build is required to read from the test content providers.