Avoid potential negative array index access to cached text.
[LibreOffice.git] / android / README.md
blob919122df38d1411d3cae563f2836ec269a8c77d4
1 # LibreOffice for Android
3 ## Bootstrap
5 Contains common code for all projects on Android to bootstrap LibreOffice. In
6 addition it is a home to `LibreOfficeKit` (LOK - see `libreofficekit/README.md`) JNI
7 classes.
9 ## Stuff in Source Directory
11 LibreOffice Android application - the code is based on Fennec (Firefox for Android).
12 It uses OpenGL ES 2 for rendering of the document tiles which are gathered from
13 LibreOffice using LOK. The application contains the LibreOffice core in one shared
14 library: `liblo-native-code.so`, which is bundled together with the application.
16 ## Architecture and Threading
18 The application implements editing support using 4 threads:
20 1. The Android UI thread, we can't perform anything here that would take a considerable
21    amount of time.
22 2. An OpenGL thread which contains the OpenGL context and is responsible for drawing
23    all layers (including tiles) to the screen.
24 3. A thread (`LOKitThread`), that performs `LibreOfficeKit` calls, which may take more time
25    to complete. In addition it also receives events from the soffice thread (see below)
26    when the callback emits an event. Events are stored in a blocking queue (thread
27    processes events in FCFS order, goes to sleep when no more event is available and
28    awakens when there are events in the queue again).
29 4. A native thread created by LibreOfficeKit (we call it the soffice thread), where
30    LibreOffice itself runs. It receives calls from `LOKitThread`, and may emit callback
31    events as necessary.
33 ## LOKitThread
35 `LOKitThread` (`org.libreoffice.LOKitThread`) communicates with LO via JNI (this can
36 be done only for one thread) and processes events (defined in `org.libreoffice.LOEvent`)
37 triggered from UI.
39 ## Application Overview
41 LibreOfficeMainActivity (`org.libreoffice.LibreOfficeMainActivity`) is the entry point
42 of the application - everything starts up and tears down from here (`onCreate`, `onResume`,
43 `onPause`, `onStart`, `onStop`, `onDestroy`).
45 ### Document View
47 From here on one of the most interesting pieces are the classes around document view,
48 which includes listening to touch events, recalculating the viewport, tiled handling
49 and rendering the layers to the document.
51 `Viewport` - the viewport is the currently visible part of the document. It is defined
52            by view rectangle and zoom.
54 `Layers` - document view is rendered using many layers. Such layers are: document
55          background, scroll handles, and also the document tiles.
57 ### Document View Classes
59 - `LayerView` (`org.mozilla.gecko.gfx.LayerView`) is the document view of the application.
60   It uses the `SurfaceView` (`android.view.SurfaceView`) as the main surface to draw on
61   using OpenGL ES 2.
63 - `GLController` (`org.mozilla.gecko.gfx.GLController`) - holder of the OpenGL context.
65 - `RenderControllerThread` (`org.mozilla.gecko.gfx.RenderControllerThread`) executes the
66   rendering requests through LayerRenderer.
68 - `LayerRenderer` (`org.mozilla.gecko.gfx.LayerRenderer`) renders all the layers.
70 - `GeckoLayerClient` (`org.mozilla.gecko.gfx.GeckoLayerClient`) is the middle man of the
71   application, which connects all the bits together. It is the document view layer
72   holder so the any management (including tiled rendering) usually go through this
73   class. It listens to draw requests and viewport changes from `PanZoomController`
74   (see "Touch events").
76 ### Touch Events, Scrolling and Zooming
78 The main class that handles the touch event, scrolling and zooming is `JavaPanZoomController`
79 `org.mozilla.gecko.gfx.JavaPanZoomController` (implementation of `PanZoomController` interface).
80 When the user performs a touch action, the document view needs to change, which means the
81 viewport changes. `JavaPanZoomController` changes the viewport and signals the change through
82 `PanZoomTarget` (`org.mozilla.gecko.gfx.PanZoomTarget`).
84 ### Tiled Rendering
86 Tiled rendering is a technique that splits the document to bitmaps of same size (typically
87 256x256) which are fetched on demand.
89 In the application the `ComposedTileLayer` (`org.mozilla.gecko.gfx.ComposedTileLayer`) is the
90 layer responsible for tracking and managing the tiles. Tiles are in this case also layers
91 (sub layers?) implemented in `SubTile` (`org.mozilla.gecko.gfx.SubTile`), where each one is
92 responsible for one tile bitmap (actually OpenGL texture once it has been uploaded).
94 When the viewport changes, the request for tile rechecking is send to `LOKitThread` (see
95 LOKitThread#tileReevaluationRequest), where the tiles are rechecked, add and removed if
96 necessary.
98 `CompositeTileLayer` is actually an abstract class, which has two implementations. One is
99 `DynamicTileLayer` (`org.mozilla.gecko.gfx.DynamicTileLayer`), which is used for main tile
100 view of the document, and `FixedZoomTileLayer` (`org.mozilla.gecko.gfx.FixedZoomTileLayer`),
101 which just renders the tiles at a fixed zoom level. This is then used as a background
102 low resolution layer.
104 ### Tile Invalidation
106 Tile can change in LibreOffice when user changes the content (adds, removes text or changes
107 the properties). In this case, an invalidation rectangle is signaled from LibreOffice, which
108 includes a rectangle that needs to be invalidated. In this case `LOKitThread` gets this request
109 via callback, and rechecks all tiles if they need to be invalidated. For more details see
110 LOKitThread#tileInvalidation).
112 ## Editing
114 For editing there are 2 coarse tasks that the LibreOffice app must do:
116 1. send input events to LibreOffice core (keyboard, touch and mouse)
117 2. listen to messages (provided via callback) from LibreOffice core and react accordingly
119 In most cases when an input event happens and is send to the LO core, then a message from
120 LO core follows. For example: when the user writes to the keyboard, key event is sent and
121 an invalidation request from LO core follows. When user touches an image, a mouse event is
122 sent, and a "new graphic selection" message from LO core follows.
124 All keyboard and touch events are sent to `LOKitThread` as `LOEvents`. In `LOKitThread` they are
125 processed and sent to LibreOffice core. The touch events originate in `JavaPanZoomController`,
126 the keyboard events in `LOKitInputConnectionHandler` (`org.libreoffice.LOKitInputConnectionHandler`),
127 however there are other parts too - depending on the need.
129 `InvalidationHandler` (`org.libreoffice.InvalidationHandler`) is the class that is responsible
130 to process messages from LibreOffice core and to track the state.
132 ## Overlay
134 Overlay elements like cursor and selections aren't drawn by the LO core, instead the core
135 only provides data (cursor position, selection rectangles) and the app needs to draw them.
136 `DocumentOverlay` (`org.libreoffice.overlay.DocumentOverlay`) and `DocumentOverlayView`
137 (`org.libreoffice.overlay.DocumentOverlayView`) are the classes that provide the overlay over
138 the document, where selections and the cursor is drawn.
141 ## Icons
143 App uses material design icons available at [1].
146 [1] - <https://www.google.com/design/icons/>
148 ## Emulator and Debugging Notes
150 For instructions on how to build for Android, see `README.cross`.
152 ### Getting Something Running
154 Attach your device, so 'adb devices' shows it. Then run:
156         cd android/source
157         make install
158         adb logcat
160 and if all goes well, you should have some nice debug output to enjoy when you
161 start the app.
163 ### Using the Emulator
165 Create an AVD in the android UI, don't even try to get the data partition size
166 right in the GUI, that is doomed to producing an AVD that doesn't work.
167 Instead start it from the console:
169         LD_LIBRARY_PATH=$(pwd)/lib emulator-arm -avd <Name> -partition-size 500
171 where <Name> is the literal name of the AVD that you entered.
173 [ In order to have proper acceleration, you need the 32-bit `libGL.so`:
175         sudo zypper in Mesa-libGL-devel-32bit
177 and run emulator-arm after the installation. ]
179 Then you can run `ant`/`adb` as described above.
181 After a while of this loop you might find that you have lost a lot of
182 space on your emulator's or device's `/data` volume. You can do:
184         adb shell stop; adb shell start
186 ## Debugging
188 First of all, you need to configure the build with `--enable-debug` or
189 `--enable-dbgutil`.  You may want to provide `--enable-symbols` to limit debuginfo,
190 like `--enable-symbols="sw/"` or so, in order to fit into the memory
191 during linking.
193 Building with all symbols is also possible but the linking is currently
194 slow (around 10 to 15 minutes) and you need lots of memory (around 16GB + some
195 swap).
197 ### Using ndk-gdb
199 Direct support for using `ndk-gdb` has been removed from the build system. It is
200 recommended that you give the lldb debugger a try that has the benefit of being
201 nicely integrated into Android Studio (see below for instructions).
202 If you nevertheless want to continue using `ndk-gdb`, use the following steps
203 that are described in more detail here: <https://stackoverflow.com/a/10539883>
205 - add `android:debuggable="true"` to `AndroidManifest.xml`
206 - push `gdbserver` to device, launch and attach to application
207 - forward debugging port from host to device
208 - launch matching gdb on host and run following setup commands:
209         - set solib-search-path obj/local/<appAbi>
210         - file obj/local/<appAbi>/liblo-native-code.so
211         - target remote :<portused>
213 Pretty printers aren't loaded automatically due to the single shared
214 object, but you can still load them manually. E.g. to have a pretty-printer for
215 `rtl::OString`, you need:
217         (gdb) python sys.path.insert(0, "/master/solenv/gdb")
218         (gdb) source /master/instdir/program/libuno_sal.so.3-gdb.py
220 ### Using Android Studio (and Thus lldb)
222 Note that lldb might not yield the same results as `ndk-gdb`. If you suspect a
223 problem with `lldb`, you can try to manually use `ndk-gdb` as described above.
224 Using `lldb` from within Android Studio is more comfortable though and works like this:
226 - open `android/source/build.gradle` in Android Studio via File|New → Import Project
227 - make sure you select the right build variant (`strippedUIDebug` is what you want)
228 - use Run|Edit Configurations to create a new configuration of type "Android Native"
229         - on tab "General" pick module "source"
230         - on tab "Native Debugger" add `android/obj/local/<hostarch>` to
231         the Symbol directories
232         - on the LLDB startup commands tab add
233         "command script import `/path/to/solenv/lldb/libreoffice/LO.py`"
234         to get some pretty printing hooks for the various string classes
236 Then you can select your new configuration and use Run | Debug to launch it.
237 Note that `lldb` doesn't initially stop execution, so if you want to add
238 breakpoints using lldb prompt, you manually have to pause execution, then you
239 can switch to the lldb tab and add your breakpoints. However making use of the
240 editor just using File|Open .. to open the desired file in Android Studio and
241 then toggling the breakpoint by clicking on the margin is more comfortable.
243 ### Debugging the Java Part
245 Open `android/source/build.gradle` in Android studio via File|New → Import
246 Project and you can use Android Studio's debugging interface.
247 Just make sure you pick the correct build variant (strippedUIDebug)
249 The alternative is to use the jdb command-line debugger. Steps to use it:
251 1. Find out the JDWP ID of a debuggable application:
253         adb jdwp
255     From the list of currently active JDWP processes, the last number is the just
256 started debuggable application.
258 2. Forward the remote JDWP port/process ID to a local port:
260         adb forward tcp:7777 jdwp:31739
262 3. Connect to the running application:
264         jdb -sourcepath src/java/ -attach localhost:7777
266 Assuming that you're already in the LOAndroid3 directory in your shell.
268 ### Debugging the Missing Services
270 Android library only include essential services that are compiled for
271 LibreOffice in order to reduce the size of the apk. When developing,
272 some services might become useful and we should add those services
273 to the combined library.
275 In order to identify missing services, we need to be able to receive
276 `SAL_INFO` from `cppuhelper/source/shlib.cxx` in logcat and therefore identify
277 what services are missing. To do so, you may want add the following
278 when configuring the build.
280     --enable-symbols="cppuhelper/ sal/"
282 [TODO: This is nonsense. `--enable-symbols` enables the `-g` option, not `SAL_INFO`.
283 Perhaps this was a misunderstanding of meaning of `--enable-selective-debuginfo`,
284 the old name for the option.]
286 Which services are combined in the android lib is determined by
288     solenv/bin/native-code.py
290 ### Common Errors / Gotchas
292     lo_dlneeds: Could not read ELF header of /data/data/org.libreoffice...libfoo.so
293 This (most likely) means that the install quietly failed, and that
294 the file is truncated; check it out with `adb shell ls -l /data/data/...`
296 ### Startup Details
298 All Android apps are basically Java programs. They run "in" a Dalvik
299 (or on Android 5 or newer - ART) virtual machine. Yes, you can also
300 have apps where all *your* code is native code, written in a compiled
301 language like C or C++. But also such apps are actually started
302 by system-provided Java bootstrapping code (`NativeActivity`) running
303 in a Dalvik VM.
305 Such a native app (or actually, "activity") is not built as a
306 executable program, but as a shared object. The Java `NativeActivity`
307 bootstrapper loads that shared object with dlopen.
309 Anyway, our current "experimental" apps are not based on `NativeActivity`.
310 They have normal Java code for the activity, and just call out to a single,
311 app-specific native library (called `liblo-native-code.so`) to do all the
312 heavy lifting.