Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 1 | # Servicifying Chromium Features |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 2 | |
| 3 | [TOC] |
| 4 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 5 | ## Overview |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 6 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 7 | Much to the dismay of Chromium developers, practicing linguists, and keyboard |
| 8 | operators everywhere, the term **servicificificification** [sic] has been |
| 9 | egregiously smuggled into the Chromium parlance. |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 10 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 11 | Lots of Chromium code is contained in reasonably well-isolated component |
| 12 | libraries with some occasionally fuzzy boundaries and often a surprising number |
| 13 | of gnarly runtime interdependencies among a complex graph of components. Y |
| 14 | implements one of Z's delegate interfaces, while X implements one of Y's |
| 15 | delegate interfaces, and now it's possible for some ridiculous bug to creep in |
| 16 | where W calls into Z at the wrong time and causes a crash in X. Yikes. |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 17 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 18 | Servicification embodies the ongoing process of **servicifying** Chromium |
| 19 | features and subsystems, or refactoring these collections of library code into |
| 20 | services with well-defined public API boundaries and very strong runtime |
| 21 | isolation via Mojo interfaces. |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 22 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 23 | The primary goals are to improve maintainability and extensibility of the system |
| 24 | over time, while also allowing for more flexible runtime configuration. For |
| 25 | example, with the Network Service in place we can now run the entire network |
| 26 | stack either inside or outside of the browser process with the flip of a |
| 27 | command-line switch. Client code using the Network Service stays the same, |
| 28 | independent of that switch. |
Oksana Zhuravlova | c6e0f95 | 2018-09-25 03:25:04 | [diff] [blame] | 29 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 30 | This document focuses on helpful guidelines and patterns for servicifying parts |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 31 | of Chromium. |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 32 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 33 | Also see general [Mojo & Services](/docs/README.md#Mojo-Services) |
| 34 | documentation for other introductory guides, API references, *etc.* |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 35 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 36 | ## Setting Up The Service |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 37 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 38 | This section briefly covers early decisions and implementation concerns when |
| 39 | introducing a new service. |
Ken Rockot | d2ed25a | 2018-11-06 22:25:37 | [diff] [blame] | 40 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 41 | ### Where in the Tree? |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 42 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 43 | Based on the |
| 44 | [service development guidelines](/services/README.md), any service which could |
| 45 | be reasonably justified as a core system service in a hypothetical, |
| 46 | well-designed operating system may belong in the top-level `//services` |
| 47 | directory. If that sounds super hand-wavy and unclear, that's because it is! |
| 48 | There isn't really a great universal policy here, so when in doubt, contact your |
| 49 | favorite local |
| 50 | [[email protected]](https://groups.google.com/a/chromium.org/forum#!forum/services-dev) |
| 51 | mailing list and start a friendly discussion. |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 52 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 53 | Other common places where developers place services, and why: |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 54 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 55 | - `//components/services` for services which haven't yet made the cut for |
| 56 | `//services` but which are either used by Content directly or by multiple |
| 57 | Content embedders. |
| 58 | - `//chrome/services` for services which are used exclusively within Chrome and |
| 59 | not shared with other Content embedders. |
| 60 | - `//chromeos/services` for services which are used on Chrome OS by more than |
| 61 | just Chrome itself (for example, if the `ash` service must also connect to |
| 62 | them for use in system UI). |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 63 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 64 | ### Launching Service Processes |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 65 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 66 | Content provides a simple |
| 67 | [`ServiceProcessHost`](https://cs.chromium.org/chromium/src/content/public/browser/service_process_host.h?rcl=723edf64a56ef6058e886afc67adc786bea39e78&l=47) |
| 68 | API to launch a new Service Process. The Mojo Remote corresponding to each |
| 69 | process launch is effectively a lifetime control for the launched process. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 70 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 71 | You may choose to maintain only a single concurrent instance of your service |
| 72 | at a time, similar to the Network or Storage services. In this case, typically |
| 73 | you will have some browser code maintain a lazy Mojo Remote to the service |
| 74 | process, and any clients of the service will have their connections brokered |
| 75 | through this interface. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 76 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 77 | In other cases you may want to manage multiple independent service processes. |
| 78 | The Data Decoder service, for example, allows for arbitrary browser code |
| 79 | to launch a unique isolated instance to process a single decode operation or |
| 80 | a batch of related operations (e.g. to decode a bunch of different objects |
| 81 | from the same untrusted origin). |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 82 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 83 | Insofar as the browser can use ServiceProcessLauncher however it likes, and the |
| 84 | corresponding Mojo Remotes can be owned just like any other object, developers |
| 85 | are free to manage their service instances however they like. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 86 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 87 | ### Hooking Up the Service Implementation |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 88 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 89 | For out-of-process service launching, Content uses its "utility" process type. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 90 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 91 | For services known to content, this is accomplished by adding an appropriate |
| 92 | factory function to |
| 93 | [`//content/utility/services.cc`](https://cs.chromium.org/chromium/src/content/utility/services.cc) |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 94 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 95 | For other services known only to Chrome, we have a similar file at |
| 96 | [`//chrome/utility/services.cc`](https://cs.chromium.org/chromium/src/chrome/utility/services.cc). |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 97 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 98 | Once an appropriate service factory is registered for your main service |
| 99 | interface in one of these places, `ServiceProcessHost::Launch` can be used to |
| 100 | acquire a new isolated instance from within the browser process. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 101 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 102 | To run a service in-process, you can simply instantiate your service |
| 103 | implementation (e.g. on a background thread) like you would any other object, |
| 104 | and you can then bind a Mojo Remote which is connected to that instance. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 105 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 106 | This is useful if you want to avoid the overhead of extra processes in some |
| 107 | scenarios, and it allows the detail of where and how the service runs to be |
| 108 | fully hidden behind management of the main interface's Mojo Remote. |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 109 | |
| 110 | ## Incremental Servicification |
| 111 | |
| 112 | For large Chromium features it is not feasible to convert an entire subsystem |
| 113 | to a service all at once. As a result, it may be necessary for the subsystem |
| 114 | to spend a considerable amount of time (weeks or months) split between the old |
| 115 | implementation and your beautiful, sparkling new service implementation. |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 116 | |
| 117 | In creating your service, you likely have two goals: |
| 118 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 119 | - Making the service available to its consumers |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 120 | - Making the service self-contained |
| 121 | |
| 122 | Those two goals are not the same, and to some extent are at tension: |
| 123 | |
| 124 | - To satisfy the first, you need to build out the API surface of the service to |
| 125 | a sufficient degree for the anticipated use cases. |
| 126 | |
| 127 | - To satisfy the second, you need to convert all clients of the code that you |
| 128 | are servicifying to instead use the service, and then fold that code into the |
| 129 | internal implementation of the service. |
| 130 | |
| 131 | Whatever your goals, you will need to proceed incrementally if your project is |
| 132 | at all non-trivial (as they basically all are given the nature of the effort). |
| 133 | You should explicitly decide what your approach to incremental bringup and |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 134 | conversion will be. Here are some approaches that have been taken for various |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 135 | services: |
| 136 | |
| 137 | - Build out your service depending directly on existing code, |
| 138 | convert the clients of that code 1-by-1, and fold the existing code into the |
| 139 | service implementation when complete ([Identity Service](https://docs.google.com/document/d/1EPLEJTZewjiShBemNP5Zyk3b_9sgdbrZlXn7j1fubW0/edit)). |
| 140 | - Build out the service with new code and make the existing code |
| 141 | into a client library of the service. In that fashion, all consumers of the |
| 142 | existing code get converted transparently ([Preferences Service](https://docs.google.com/document/d/1JU8QUWxMEXWMqgkvFUumKSxr7Z-nfq0YvreSJTkMVmU/edit#heading=h.19gc5b5u3e3x)). |
| 143 | - Build out the new service piece-by-piece by picking a given |
| 144 | bite-size piece of functionality and entirely servicifying that functionality |
| 145 | ([Device Service](https://docs.google.com/document/d/1_1Vt4ShJCiM3fin-leaZx00-FoIPisOr8kwAKsg-Des/edit#heading=h.c3qzrjr1sqn7)). |
| 146 | |
| 147 | These all have tradeoffs: |
| 148 | |
Ken Rockot | d2ed25a | 2018-11-06 22:25:37 | [diff] [blame] | 149 | - The first lets you incrementally validate your API and implementation, but |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 150 | leaves the service depending on external code for a long period of time. |
| 151 | - The second can create a self-contained service more quickly, but leaves |
| 152 | all the existing clients in place as potential cleanup work. |
| 153 | - The third ensures that you're being honest as you go, but delays having |
| 154 | the breadth of the service API up and going. |
| 155 | |
| 156 | Which makes sense depends both on the nature of the existing code and on |
| 157 | the priorities for doing the servicification. The first two enable making the |
| 158 | service available for new use cases sooner at the cost of leaving legacy code in |
| 159 | place longer, while the last is most suitable when you want to be very exacting |
| 160 | about doing the servicification cleanly as you go. |
| 161 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 162 | ## Platform-Specific Issues: Android |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 163 | |
Ken Rockot | d2ed25a | 2018-11-06 22:25:37 | [diff] [blame] | 164 | As you servicify code running on Android, you might find that you need to port |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 165 | interfaces that are served in Java. Here is an |
| 166 | [example CL](https://codereview.chromium.org/2643713002) that gives a basic |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 167 | pattern to follow in doing this. |
| 168 | |
| 169 | You also might need to register JNI in your service. That is simple to set |
| 170 | up, as illustrated in [this CL](https://codereview.chromium.org/2690963002). |
| 171 | (Note that that CL is doing more than *just* enabling the Device Service to |
| 172 | register JNI; you should take the register_jni.cc file added there as your |
| 173 | starting point to examine the pattern to follow). |
| 174 | |
| 175 | Finally, it is possible that your feature will have coupling to UI process state |
| 176 | (e.g., the Activity) via Android system APIs. To handle this challenging |
| 177 | issue, see the section on [Coupling to UI](#Coupling-to-UI). |
| 178 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 179 | ## Platform-Specific Issues: iOS |
| 180 | |
| 181 | *** aside |
| 182 | **WARNING:** Some of this content is obsolete and needs to be updated. When in |
| 183 | doubt, look approximately near the recommended bits of code and try to find |
| 184 | relevant prior art. |
| 185 | *** |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 186 | |
Ken Rockot | 216eb5d | 2020-02-19 17:09:55 | [diff] [blame] | 187 | Services are supported on iOS insofar as Mojo is supported. However, Chrome on |
| 188 | iOS is strictly single-process, and all services thus must run in-process on |
| 189 | iOS. |
Colin Blundell | 08b84ab | 2017-09-04 16:10:04 | [diff] [blame] | 190 | |
| 191 | If you have a use case or need for services on iOS, contact |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 192 | [email protected]. For general information on the motivations and vision for |
| 193 | supporting services on iOS, see the high-level |
| 194 | [servicification design doc](https://docs.google.com/document/d/15I7sQyQo6zsqXVNAlVd520tdGaS8FCicZHrN0yRu-oU/edit). |
| 195 | In particular, search for the mentions of iOS within the doc. |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 196 | |
| 197 | ## Client-Specific Issues |
| 198 | |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 199 | #### Mocking Interface Impls in JS |
Kent Tamura | 59ffb02 | 2018-11-27 05:30:56 | [diff] [blame] | 200 | It is a common pattern in Blink's web tests to mock a remote Mojo interface |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 201 | in JS so that native Blink code requests interfaces from the test JS rather |
| 202 | than whatever would normally service them in the browser process. |
| 203 | |
| 204 | The current way to set up that sort of thing looks like |
| 205 | [this](https://cs.chromium.org/chromium/src/third_party/blink/web_tests/battery-status/resources/mock-battery-monitor.js?rcl=be6e0001855f7f1cfc26205d0ff5a2b5b324fcbd&l=19). |
| 206 | |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 207 | #### Feature Impls That Depend on Blink Headers |
| 208 | In the course of servicifying a feature that has Blink as a client, you might |
| 209 | encounter cases where the feature implementation has dependencies on Blink |
| 210 | public headers (e.g., defining POD structs that are used both by the client and |
| 211 | by the feature implementation). These dependencies pose a challenge: |
| 212 | |
Ken Rockot | d2ed25a | 2018-11-06 22:25:37 | [diff] [blame] | 213 | - Services should not depend on Blink, as this is a dependency inversion (Blink |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 214 | is a client of services). |
| 215 | - However, Blink is very careful about accepting dependencies from Chromium. |
| 216 | |
| 217 | To meet this challenge, you have two options: |
| 218 | |
| 219 | 1. Move the code in question from C++ to mojom (e.g., if it is simple structs). |
Ken Rockot | d2ed25a | 2018-11-06 22:25:37 | [diff] [blame] | 220 | 2. Move the code into the service's C++ client library, being very explicit |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 221 | about its usage by Blink. See |
| 222 | [this CL](https://codereview.chromium.org/2415083002) for a basic pattern to |
| 223 | follow. |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 224 | |
| 225 | #### Frame-Scoped Connections |
| 226 | You must think carefully about the scoping of the connection being made |
| 227 | from Blink. In particular, some feature requests are necessarily scoped to a |
| 228 | frame in the context of Blink (e.g., geolocation, where permission to access the |
| 229 | interface is origin-scoped). Servicifying these features is then challenging, as |
| 230 | Blink has no frame-scoped connection to arbitrary services (by design, as |
| 231 | arbitrary services have no knowledge of frames or even a notion of what a frame |
| 232 | is). |
| 233 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 234 | After a |
| 235 | [long discussion](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/CSnDUjthAuw), |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 236 | the policy that we have adopted for this challenge is the following: |
| 237 | |
| 238 | CURRENT |
| 239 | |
| 240 | - The renderer makes a request through its frame-scoped connection to the |
| 241 | browser. |
| 242 | - The browser obtains the necessary permissions before directly servicing the |
| 243 | request. |
| 244 | |
| 245 | AFTER SERVICIFYING THE FEATURE IN QUESTION |
| 246 | |
| 247 | - The renderer makes a request through its frame-scoped connection to the |
| 248 | browser. |
| 249 | - The browser obtains the necessary permissions before forwarding the |
| 250 | request on to the underlying service that hosts the feature. |
| 251 | |
| 252 | Notably, from the renderer's POV essentially nothing changes here. |
| 253 | |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 254 | ## Strategies for Challenges to Decoupling from //content |
| 255 | |
| 256 | ### Coupling to UI |
| 257 | |
| 258 | Some feature implementations have hard constraints on coupling to UI on various |
| 259 | platforms. An example is NFC on Android, which requires the Activity of the view |
| 260 | in which the requesting client is hosted in order to access the NFC platform |
| 261 | APIs. This coupling is at odds with the vision of servicification, which is to |
| 262 | make the service physically isolatable. However, when it occurs, we need to |
| 263 | accommodate it. |
| 264 | |
| 265 | The high-level decision that we have reached is to scope the coupling to the |
| 266 | feature *and* platform in question (rather than e.g. introducing a |
| 267 | general-purpose FooServiceDelegate), in order to make it completely explicit |
| 268 | what requires the coupling and to avoid the coupling creeping in scope. |
| 269 | |
| 270 | The basic strategy to support this coupling while still servicifying the feature |
| 271 | in question is to inject a mechanism of mapping from an opaque "context ID" to |
| 272 | the required context. The embedder (e.g., //content) maintains this map, and the |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 273 | service makes use of it. The embedder also serves as an intermediary: it |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 274 | provides a connection that is appropriately context-scoped to clients. When |
| 275 | clients request the feature in question, the embedder forwards the request on |
| 276 | along with the appropriate context ID. The service impl can then map that |
| 277 | context ID back to the needed context on-demand using the mapping functionality |
| 278 | injected into the service impl. |
| 279 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 280 | To make this more concrete, see |
| 281 | [this CL](https://codereview.chromium.org/2734943003). |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 282 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 283 | ### Shutdown of Singletons |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 284 | |
| 285 | You might find that your feature includes singletons that are shut down as part |
| 286 | of //content's shutdown process. As part of decoupling the feature |
| 287 | implementation entirely from //content, the shutdown of these singletons must be |
| 288 | either ported into your service or eliminated: |
| 289 | |
| 290 | - In general, as Chromium is moving away from graceful shutdown, the first |
| 291 | question to analyze is: Do the singletons actually need to be shut down at |
| 292 | all? |
Ken Rockot | d2ed25a | 2018-11-06 22:25:37 | [diff] [blame] | 293 | - If you need to preserve shutdown of the singleton, the naive approach is to |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 294 | move the shutdown of the singleton to the destructor of your service |
Ken Rockot | d2ed25a | 2018-11-06 22:25:37 | [diff] [blame] | 295 | - However, you should carefully examine when your service is destroyed compared |
| 296 | to when the previous code was executing, and ensure that any differences |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 297 | introduced do not impact correctness. |
| 298 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 299 | See |
| 300 | [this thread](https://groups.google.com/a/chromium.org/forum/#!topic/services-dev/Y9FKZf9n1ls) |
| 301 | for more discussion of this issue. |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 302 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 303 | ## Additional Support |
blundell | 77afe7a6 | 2017-05-02 09:08:49 | [diff] [blame] | 304 | |
Ken Rockot | ab03512 | 2019-02-06 00:35:24 | [diff] [blame] | 305 | If this document was not helpful in some way, please post a message to your |
| 306 | friendly local |
| 307 | [[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/chromium-mojo) |
| 308 | or |
| 309 | [[email protected]](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev) |
| 310 | mailing list. |