summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/update/docs/BackgroundUpdates.rst
blob: 7f97f58c7472d8bbafc70190687947fc06c44399 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
==================
Background Updates
==================

The purpose of the background update system is to perform application updates
during times when Firefox is not running. It was originally implemented in `bug
1689520 <https://bugzilla.mozilla.org/show_bug.cgi?id=1689520>`__.

The system has three main tasks it needs to handle:

1. :ref:`Determining whether background updates are possible <background-updates-determining>`

2. :ref:`Scheduling background tasks <background-updates-scheduling>`

3. :ref:`Checking for updates <background-updates-checking>`

Architecturally, the background task is an instance of Firefox running in a
special background mode, not a separate tool. This allows it to leverage
existing functionality in Firefox, including the existing update code, but also
keep acceptable performance characteristics for a background task by controlling
and limiting the parts of Firefox that are loaded.

Everything in this document applies only to Microsoft Windows systems. In the
future, we would like to extend background update support to macOS (see `bug
1653435 <https://bugzilla.mozilla.org/show_bug.cgi?id=1653435>`__), however
support for Linux and other Unix variants is not planned due to the variation in
OS-level scheduling affordances across distributions/configurations.

Lifecycle
=========

When background updates are possible, the background update task will be invoked
every 7 hours (by default).  The first invocation initiates an update download
which proceeds after the task exits using Windows BITS.  The second invocation
prepares and stages the update. Since `bug 1704855 <https://bugzilla.mozilla.org/show_bug.cgi?id=1704855>`__,
this second invocation restarts automatically and installs the update as it
starts up, and then checks for a newer update, possibly initiating another update
download.  The cycle then continues. If the user launches Firefox at any point
in this process, it will take over. If the background update task is invoked
while Firefox proper is running, the task exits without doing any work.

.. _background-updates-determining:

Determining whether background updates are possible
===================================================

Configuration
-------------

Updating Firefox, by definition, is an operation that applies to a Firefox
installation. However, Firefox configuration is generally done via preference
values and other files which are stored in a Firefox profile, and in general
profiles do not correspond 1:1 with installations. This raises the question of
how the configuration for something like the background updater should be
managed. We deal with this question in two different ways.

There are two main preferences specifically relevant to updates. Those
are ``app.update.auto``, which controls whether updates should be
downloaded automatically at all, even if Firefox is running, and
``app.update.background.enabled``, to specifically control whether to
use the background update system. We store these preferences in the
update root directory, which is located in a per-installation location
outside of any profile. Any profile loaded in that installation can
observe and control these settings.

But there are some other pieces of state which absolutely must come from a
profile, such as the telemetry client ID and logging level settings (see
`BackgroundTasksUtils.jsm <https://searchfox.org/mozilla-central/source/toolkit/components/backgroundtasks/BackgroundTasksUtils.jsm>`__).

This means that, in addition to our per-installation prefs, we also need
to be able to identify and load a profile. To do that, we leverage `the profile
service <https://searchfox.org/mozilla-central/source/toolkit/profile/nsIToolkitProfileService.idl>`__
to determine what the default profile for the installation would be if we were
running a normal browser session, and the background updater always uses it.

Criteria
--------

The default profile must satisfy several conditions in order for background
updates to be scheduled. None of these confounding factors are present in fully
default configurations, but some are relatively common. See
`BackgroundUpdate.REASON <https://searchfox.org/mozilla-central/search?q=symbol:BackgroundUpdate%23REASON>`__
for all the details.

In order for the background task to be scheduled:

-  The per-installation ``app.update.background.enabled`` pref must be
   true

-  The per-installation ``app.update.auto`` pref must be true (the
   default)

-  The installation must have been created by an installer executable and not by
   manually extracting an archive file

-  The current OS user must be capable of updating the installation based on its
   file system permissions, either by having permission to write to application
   files directly or by using the Mozilla Maintenance Service (which also
   requires that it be installed and enabled, as it is by default)

-  BITS must be enabled via ``app.update.BITS.enabled`` (the default)

-  Firefox proxy server settings must not be configured (the default)

-  ``app.update.langpack.enabled`` must be false, or otherwise there must be no
   langpacks installed. Background tasks cannot update addons such as langpacks,
   because they are installed into a profile, and langpacks that are not
   precisely matched with the version of Firefox that is installed can cause
   YSOD failures (see `bug 1647443 <https://bugzilla.mozilla.org/show_bug.cgi?id=1647443>`__),
   so background updating in the presence of langpacks is too risky.

If any per-installation prefs are changed while the default profile is not
running, the background update task will witness the changed prefs during its
next scheduled run, and exit if appropriate. The background task will not be
unscheduled at that point; that is delayed until a browser session is run with
the default profile (it should be possible for the background update task to
unschedule itself, but currently we prefer the simplicity of handling all
scheduling tasks from a single location).

In the extremely unusual case when prefs belonging to the default profile are
modified outside of Firefox (with a text editor, say), then the
background task will generally pick up those changes with no action needed,
because it will fish the changed settings directly from the profile.

.. _background-updates-scheduling:

Scheduling background tasks
===========================

We use OS-level scheduling mechanisms to schedule the command ``firefox
--backgroundtask backgroundupdate`` to run on a particular cadence. This cadence
is controlled by the ``app.update.background.interval`` preference, which
defaults to 7 hours.

On Windows, we use the `Task Scheduler
API <https://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page>`__;
on macOS this will use
`launchd <https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html>`__.
For platform-specific scheduling details, see the
`TaskScheduler.jsm <https://searchfox.org/mozilla-central/source/toolkit/components/taskscheduler/TaskScheduler.jsm>`__
module.

These background tasks are scheduled per OS user and run with that user’s
permissions. No additional privileges are requested or needed, regardless of the
user account's status, because we have already verified that either the user has
all the permissions they need or that the Maintenance Service can be used.

Scheduling is done from within Firefox (or a background task) itself. To
reduce shared state, only the *default* Firefox profile will interact
with the OS-level task scheduling mechanism.

.. _background-updates-checking:

Checking for updates
====================

After verifying all the preconditions and exiting immediately if any do not
hold, the ``backgroundupdate`` task then verifies that it is the only Firefox
instance running (as determined by a multi-instance lock, see `bug
1553982 <https://bugzilla.mozilla.org/show_bug.cgi?id=1553982>`__), since
otherwise it would be unsafe to continue performing any update work.

The task then fishes configuration settings from the default profile, namely:

-  A subset of update specific preferences, such as ``app.update.log``

-  Data reporting preferences, to ensure the task respects the user’s choices

-  The (legacy) Telemetry client ID, so that background update Telemetry
   can be correlated with other Firefox Telemetry

The background task creates a distinct profile for itself to load, because a
profile must be present in order for most of the Firefox code that it relies on
to function.  This distinct profile is non-ephemeral, i.e., persistent, but not
visible to users: see `bug 1775132
<https://bugzilla.mozilla.org/show_bug.cgi?id=1775132>`__

After setting up this profile and reading all the configuration we need
into it, the regular
`UpdateService.jsm <https://searchfox.org/mozilla-central/source/toolkit/mozapps/update/UpdateService.jsm>`__
check process is initiated. To the greatest extent possible, this process is
identical to what happens during any regular browsing session.

Specific topics
===============

User interface
--------------

The background update task must not produce any user-visible interface. If it
did, whatever appeared would be \*disembodied\*, unconnected to any usage of
Firefox itself and appearing to a user as a weird, scary popup that came out of
nowhere. To this end, we disable all UI within the updater when invoking
from a background task. See `bug
1696276 <https://bugzilla.mozilla.org/show_bug.cgi?id=1696276>`__.

This point also means that we cannot prompt for user elevation (on Windows this
would mean a UAC prompt) from within the task, so we have to make very sure that
we will be able to perform an update without needing to elevate. By default on
Windows we are able to do this because of the presence of the Maintenance
Service, but it may be disabled or not installed, so we still have to check.

Staging
-------

The background update task will follow the update staging setting in the user’s
default profile. The default setting is to enable staging, so most users will
have it. Background update tasks recognize when an update has been staged and
try to restart to finalize the staged update. Background tasks cannot
finalize a staged update in all cases however; for one example, see
`bug 1695797 <https://bugzilla.mozilla.org/show_bug.cgi?id=1695797>`__, where
we ensure that background tasks do not finalize a staged update while other
instances of the application are running.

Staging is enabled by default because it provides a marked improvement in
startup time for a browsing session. Without staging, browser startup following
retrieving an update would be blocked on extracting the update archive and
patching each individual application file. Staging does all of that in advance,
so that all that needs to be done to complete an update (and therefore all that
needs to be done during the startup path), is to move the already patched (that
is, staged) files into place, a much faster and less resource intensive job.