diff options
| author | Po Lu | 2023-09-03 10:04:44 +0800 |
|---|---|---|
| committer | Po Lu | 2023-09-03 10:05:15 +0800 |
| commit | 4debb110d70691a405a50272b3ca5d0a264e0010 (patch) | |
| tree | b6b3fa743030a6b31e1f15beaf246d78d85418bb /admin/notes/java | |
| parent | 8ecc73f47a7d5473d0e3f070d85051fc85580f9d (diff) | |
| download | emacs-4debb110d70691a405a50272b3ca5d0a264e0010.tar.gz emacs-4debb110d70691a405a50272b3ca5d0a264e0010.zip | |
Move Android port internals documentation to admin/notes
* admin/notes/java: New file. Move most of its contents from
README, and introduce a section on compatibility.
* java/README: Move internals to admin/notes/java.
Diffstat (limited to 'admin/notes/java')
| -rw-r--r-- | admin/notes/java | 1097 |
1 files changed, 1097 insertions, 0 deletions
diff --git a/admin/notes/java b/admin/notes/java new file mode 100644 index 00000000000..125ac0aad67 --- /dev/null +++ b/admin/notes/java | |||
| @@ -0,0 +1,1097 @@ | |||
| 1 | Installation instructions for Android | ||
| 2 | Copyright (C) 2023 Free Software Foundation, Inc. | ||
| 3 | See the end of the file for license conditions. | ||
| 4 | |||
| 5 | |||
| 6 | |||
| 7 | OVERVIEW OF JAVA | ||
| 8 | |||
| 9 | Emacs developers do not know Java, and there is no reason they should | ||
| 10 | have to. Thus, the code in this directory is confined to what is | ||
| 11 | strictly necessary to support Emacs, and only uses a subset of Java | ||
| 12 | written in a way that is easily understandable to C programmers. | ||
| 13 | |||
| 14 | Java is required because the entire Android runtime is based around | ||
| 15 | Java, and there is no way to write an Android program which runs | ||
| 16 | without Java. | ||
| 17 | |||
| 18 | This text exists to prime other Emacs developers, already familar with | ||
| 19 | C, on the basic architecture of the Android port, and to teach them | ||
| 20 | how to read and write the Java code found in this directory. | ||
| 21 | |||
| 22 | Java is an object oriented language with automatic memory management | ||
| 23 | compiled down to bytecode, which is then subject to interpretation by | ||
| 24 | a Java virtual machine. | ||
| 25 | |||
| 26 | What that means, is that: | ||
| 27 | |||
| 28 | struct emacs_window | ||
| 29 | { | ||
| 30 | int some_fields; | ||
| 31 | int of_emacs_window; | ||
| 32 | }; | ||
| 33 | |||
| 34 | static void | ||
| 35 | do_something_with_emacs_window (struct emacs_window *a, int n) | ||
| 36 | { | ||
| 37 | a->some_fields = a->of_emacs_window + n; | ||
| 38 | } | ||
| 39 | |||
| 40 | would be written: | ||
| 41 | |||
| 42 | public class EmacsWindow | ||
| 43 | { | ||
| 44 | public int someFields; | ||
| 45 | public int ofEmacsWindow; | ||
| 46 | |||
| 47 | public void | ||
| 48 | doSomething (int n) | ||
| 49 | { | ||
| 50 | someFields = ofEmacsWindow + n; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | and instead of doing: | ||
| 55 | |||
| 56 | do_something_with_emacs_window (my_window, 1); | ||
| 57 | |||
| 58 | you say: | ||
| 59 | |||
| 60 | myWindow.doSomething (1); | ||
| 61 | |||
| 62 | In addition to functions associated with an object of a given class | ||
| 63 | (such as EmacsWindow), Java also has two other kinds of functions. | ||
| 64 | |||
| 65 | The first are so-called ``static'' functions (the static means | ||
| 66 | something entirely different from what it does in C.) | ||
| 67 | |||
| 68 | A static function, while still having to be defined within a class, | ||
| 69 | can be called without any object. Instead of the object, you write | ||
| 70 | the name of the Java class within which it is defined. For example, | ||
| 71 | the following C code: | ||
| 72 | |||
| 73 | int | ||
| 74 | multiply_a_with_b_and_then_add_c (int a, int b, int c) | ||
| 75 | { | ||
| 76 | return a * b + c; | ||
| 77 | } | ||
| 78 | |||
| 79 | would be: | ||
| 80 | |||
| 81 | public class EmacsSomething | ||
| 82 | { | ||
| 83 | public static int | ||
| 84 | multiplyAWithBAndThenAddC (int a, int b, int c) | ||
| 85 | { | ||
| 86 | return a * b + c; | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | Then, instead of calling: | ||
| 91 | |||
| 92 | int foo; | ||
| 93 | |||
| 94 | foo = multiply_a_with_b_then_add_c (1, 2, 3); | ||
| 95 | |||
| 96 | you say: | ||
| 97 | |||
| 98 | int foo; | ||
| 99 | |||
| 100 | foo = EmacsSomething.multiplyAWithBAndThenAddC (1, 2, 3); | ||
| 101 | |||
| 102 | In Java, ``static'' does not mean that the function is only used | ||
| 103 | within its compilation unit! Instead, the ``private'' qualifier is | ||
| 104 | used to mean more or less the same thing: | ||
| 105 | |||
| 106 | static void | ||
| 107 | this_procedure_is_only_used_within_this_file (void) | ||
| 108 | { | ||
| 109 | do_something (); | ||
| 110 | } | ||
| 111 | |||
| 112 | becomes | ||
| 113 | |||
| 114 | public class EmacsSomething | ||
| 115 | { | ||
| 116 | private static void | ||
| 117 | thisProcedureIsOnlyUsedWithinThisClass () | ||
| 118 | { | ||
| 119 | |||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | the other kind are called ``constructors''. They are functions that | ||
| 124 | must be called to allocate memory to hold a class: | ||
| 125 | |||
| 126 | public class EmacsFoo | ||
| 127 | { | ||
| 128 | int bar; | ||
| 129 | |||
| 130 | public | ||
| 131 | EmacsFoo (int tokenA, int tokenB) | ||
| 132 | { | ||
| 133 | bar = tokenA + tokenB; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | now, the following statement: | ||
| 138 | |||
| 139 | EmacsFoo foo; | ||
| 140 | |||
| 141 | foo = new EmacsFoo (1, 2); | ||
| 142 | |||
| 143 | becomes more or less equivalent to the following C code: | ||
| 144 | |||
| 145 | struct emacs_foo | ||
| 146 | { | ||
| 147 | int bar; | ||
| 148 | }; | ||
| 149 | |||
| 150 | struct emacs_foo * | ||
| 151 | make_emacs_foo (int token_a, int token_b) | ||
| 152 | { | ||
| 153 | struct emacs_foo *foo; | ||
| 154 | |||
| 155 | foo = xmalloc (sizeof *foo); | ||
| 156 | foo->bar = token_a + token_b; | ||
| 157 | |||
| 158 | return foo; | ||
| 159 | } | ||
| 160 | |||
| 161 | /* ... */ | ||
| 162 | |||
| 163 | struct emacs_foo *foo; | ||
| 164 | |||
| 165 | foo = make_emacs_foo (1, 2); | ||
| 166 | |||
| 167 | A class may have any number of constructors, or no constructors at | ||
| 168 | all, in which case the compiler inserts an empty constructor. | ||
| 169 | |||
| 170 | |||
| 171 | |||
| 172 | Sometimes, you will see Java code that looks like this: | ||
| 173 | |||
| 174 | allFiles = filesDirectory.listFiles (new FileFilter () { | ||
| 175 | @Override | ||
| 176 | public boolean | ||
| 177 | accept (File file) | ||
| 178 | { | ||
| 179 | return (!file.isDirectory () | ||
| 180 | && file.getName ().endsWith (".pdmp")); | ||
| 181 | } | ||
| 182 | }); | ||
| 183 | |||
| 184 | This is Java's version of GCC's nested function extension. The major | ||
| 185 | difference is that the nested function may still be called even after | ||
| 186 | it goes out of scope, and always retains a reference to the class and | ||
| 187 | local variables around where it was called. | ||
| 188 | |||
| 189 | Being an object-oriented language, Java also allows defining that a | ||
| 190 | class ``extends'' another class. The following C code: | ||
| 191 | |||
| 192 | struct a | ||
| 193 | { | ||
| 194 | long thirty_two; | ||
| 195 | }; | ||
| 196 | |||
| 197 | struct b | ||
| 198 | { | ||
| 199 | struct a a; | ||
| 200 | long long sixty_four; | ||
| 201 | }; | ||
| 202 | |||
| 203 | extern void do_something (struct a *); | ||
| 204 | |||
| 205 | void | ||
| 206 | my_function (struct b *b) | ||
| 207 | { | ||
| 208 | do_something (&b->a); | ||
| 209 | } | ||
| 210 | |||
| 211 | is roughly equivalent to the following Java code, split into two | ||
| 212 | files: | ||
| 213 | |||
| 214 | A.java | ||
| 215 | |||
| 216 | public class A | ||
| 217 | { | ||
| 218 | int thirtyTwo; | ||
| 219 | |||
| 220 | public void | ||
| 221 | doSomething () | ||
| 222 | { | ||
| 223 | etcEtcEtc (); | ||
| 224 | } | ||
| 225 | }; | ||
| 226 | |||
| 227 | B.java | ||
| 228 | |||
| 229 | public class B extends A | ||
| 230 | { | ||
| 231 | long sixty_four; | ||
| 232 | |||
| 233 | public static void | ||
| 234 | myFunction (B b) | ||
| 235 | { | ||
| 236 | b.doSomething (); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | the Java runtime has transformed the call to ``b.doSomething'' to | ||
| 241 | ``((A) b).doSomething''. | ||
| 242 | |||
| 243 | However, Java also allows overriding this behavior, by specifying the | ||
| 244 | @Override keyword: | ||
| 245 | |||
| 246 | public class B extends A | ||
| 247 | { | ||
| 248 | long sixty_four; | ||
| 249 | |||
| 250 | @Override | ||
| 251 | public void | ||
| 252 | doSomething () | ||
| 253 | { | ||
| 254 | Something.doSomethingTwo (); | ||
| 255 | super.doSomething (); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | now, any call to ``doSomething'' on a ``B'' created using ``new B ()'' | ||
| 260 | will end up calling ``Something.doSomethingTwo'', before calling back | ||
| 261 | to ``A.doSomething''. This override also applies in reverse; that is | ||
| 262 | to say, even if you write: | ||
| 263 | |||
| 264 | ((A) b).doSomething (); | ||
| 265 | |||
| 266 | B's version of doSomething will still be called, if ``b'' was created | ||
| 267 | using ``new B ()''. | ||
| 268 | |||
| 269 | This mechanism is used extensively throughout the Java language and | ||
| 270 | Android windowing APIs. | ||
| 271 | |||
| 272 | Elsewhere, you will encounter Java code that defines arrays: | ||
| 273 | |||
| 274 | public class EmacsFrobinicator | ||
| 275 | { | ||
| 276 | public static void | ||
| 277 | emacsFrobinicate (int something) | ||
| 278 | { | ||
| 279 | int[] primesFromSomething; | ||
| 280 | |||
| 281 | primesFromSomething = new int[numberOfPrimes]; | ||
| 282 | /* ... */ | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | Java arrays are similar to C arrays in that they can not grow. But | ||
| 287 | they are very much unlike C arrays in that they are always references | ||
| 288 | (as opposed to decaying into pointers in only some situations), and | ||
| 289 | contain information about their length. | ||
| 290 | |||
| 291 | If another function named ``frobinicate1'' takes an array as an | ||
| 292 | argument, then it need not take the length of the array. | ||
| 293 | |||
| 294 | Instead, it may simply iterate over the array like so: | ||
| 295 | |||
| 296 | int i, k; | ||
| 297 | |||
| 298 | for (i = 0; i < array.length; ++i) | ||
| 299 | { | ||
| 300 | k = array[i]; | ||
| 301 | |||
| 302 | Whatever.doSomethingWithK (k); | ||
| 303 | } | ||
| 304 | |||
| 305 | The syntax used to define arrays is also slightly different. As | ||
| 306 | arrays are always references, there is no way for you to tell the | ||
| 307 | runtime to allocate an array of size N in a structure (class.) | ||
| 308 | |||
| 309 | Instead, if you need an array of that size, you must declare a field | ||
| 310 | with the type of the array, and allocate the array inside the class's | ||
| 311 | constructor, like so: | ||
| 312 | |||
| 313 | public class EmacsArrayContainer | ||
| 314 | { | ||
| 315 | public int[] myArray; | ||
| 316 | |||
| 317 | public | ||
| 318 | EmacsArrayContainer () | ||
| 319 | { | ||
| 320 | myArray = new array[10]; | ||
| 321 | } | ||
| 322 | } | ||
| 323 | |||
| 324 | while in C, you could just have written: | ||
| 325 | |||
| 326 | struct emacs_array_container | ||
| 327 | { | ||
| 328 | int my_array[10]; | ||
| 329 | }; | ||
| 330 | |||
| 331 | or, possibly even better, | ||
| 332 | |||
| 333 | typedef int emacs_array_container[10]; | ||
| 334 | |||
| 335 | Alas, Java has no equivalent of `typedef'. | ||
| 336 | |||
| 337 | Like in C, Java string literals are delimited by double quotes. | ||
| 338 | Unlike C, however, strings are not NULL-terminated arrays of | ||
| 339 | characters, but a distinct type named ``String''. They store their | ||
| 340 | own length, characters in Java's 16-bit ``char'' type, and are capable | ||
| 341 | of holding NULL bytes. | ||
| 342 | |||
| 343 | Instead of writing: | ||
| 344 | |||
| 345 | wchar_t character; | ||
| 346 | extern char *s; | ||
| 347 | size_t s; | ||
| 348 | |||
| 349 | for (/* determine n, s in a loop. */) | ||
| 350 | s += mbstowc (&character, s, n); | ||
| 351 | |||
| 352 | or: | ||
| 353 | |||
| 354 | const char *byte; | ||
| 355 | |||
| 356 | for (byte = my_string; *byte; ++byte) | ||
| 357 | /* do something with *byte. */; | ||
| 358 | |||
| 359 | or perhaps even: | ||
| 360 | |||
| 361 | size_t length, i; | ||
| 362 | char foo; | ||
| 363 | |||
| 364 | length = strlen (my_string); | ||
| 365 | |||
| 366 | for (i = 0; i < length; ++i) | ||
| 367 | foo = my_string[i]; | ||
| 368 | |||
| 369 | you write: | ||
| 370 | |||
| 371 | char foo; | ||
| 372 | int i; | ||
| 373 | |||
| 374 | for (i = 0; i < myString.length (); ++i) | ||
| 375 | foo = myString.charAt (0); | ||
| 376 | |||
| 377 | Java also has stricter rules on what can be used as a truth value in a | ||
| 378 | conditional. While in C, any non-zero value is true, Java requires | ||
| 379 | that every truth value be of the boolean type ``boolean''. | ||
| 380 | |||
| 381 | What this means is that instead of simply writing: | ||
| 382 | |||
| 383 | if (foo || bar) | ||
| 384 | |||
| 385 | where foo can either be 1 or 0, and bar can either be NULL or a | ||
| 386 | pointer to something, you must explicitly write: | ||
| 387 | |||
| 388 | if (foo != 0 || bar != null) | ||
| 389 | |||
| 390 | in Java. | ||
| 391 | |||
| 392 | JAVA NATIVE INTERFACE | ||
| 393 | |||
| 394 | Java also provides an interface for C code to interface with Java. | ||
| 395 | |||
| 396 | C functions exported from a shared library become static Java | ||
| 397 | functions within a class, like so: | ||
| 398 | |||
| 399 | public class EmacsNative | ||
| 400 | { | ||
| 401 | /* Obtain the fingerprint of this build of Emacs. The fingerprint | ||
| 402 | can be used to determine the dump file name. */ | ||
| 403 | public static native String getFingerprint (); | ||
| 404 | |||
| 405 | /* Set certain parameters before initializing Emacs. | ||
| 406 | |||
| 407 | assetManager must be the asset manager associated with the | ||
| 408 | context that is loading Emacs. It is saved and remains for the | ||
| 409 | remainder the lifetime of the Emacs process. | ||
| 410 | |||
| 411 | filesDir must be the package's data storage location for the | ||
| 412 | current Android user. | ||
| 413 | |||
| 414 | libDir must be the package's data storage location for native | ||
| 415 | libraries. It is used as PATH. | ||
| 416 | |||
| 417 | cacheDir must be the package's cache directory. It is used as | ||
| 418 | the `temporary-file-directory'. | ||
| 419 | |||
| 420 | pixelDensityX and pixelDensityY are the DPI values that will be | ||
| 421 | used by Emacs. | ||
| 422 | |||
| 423 | classPath must be the classpath of this app_process process, or | ||
| 424 | NULL. | ||
| 425 | |||
| 426 | emacsService must be the EmacsService singleton, or NULL. */ | ||
| 427 | public static native void setEmacsParams (AssetManager assetManager, | ||
| 428 | String filesDir, | ||
| 429 | String libDir, | ||
| 430 | String cacheDir, | ||
| 431 | float pixelDensityX, | ||
| 432 | float pixelDensityY, | ||
| 433 | String classPath, | ||
| 434 | EmacsService emacsService); | ||
| 435 | } | ||
| 436 | |||
| 437 | Where the corresponding C functions are located in android.c, and | ||
| 438 | loaded by the special invocation: | ||
| 439 | |||
| 440 | static | ||
| 441 | { | ||
| 442 | System.loadLibrary ("emacs"); | ||
| 443 | }; | ||
| 444 | |||
| 445 | where ``static'' defines a section of code which will be run upon the | ||
| 446 | object (containing class) being loaded. This is like: | ||
| 447 | |||
| 448 | __attribute__((constructor)) | ||
| 449 | |||
| 450 | on systems where shared object constructors are supported. | ||
| 451 | |||
| 452 | See http://docs.oracle.com/en/java/javase/19/docs/specs/jni/intro.html | ||
| 453 | for more details. | ||
| 454 | |||
| 455 | |||
| 456 | |||
| 457 | OVERVIEW OF ANDROID | ||
| 458 | |||
| 459 | When the Android system starts an application, it does not actually | ||
| 460 | call the application's ``main'' function. It may not even start the | ||
| 461 | application's process if one is already running. | ||
| 462 | |||
| 463 | Instead, Android is organized around components. When the user opens | ||
| 464 | the ``Emacs'' icon, the Android system looks up and starts the | ||
| 465 | component associated with the ``Emacs'' icon. In this case, the | ||
| 466 | component is called an activity, and is declared in | ||
| 467 | the AndroidManifest.xml in this directory: | ||
| 468 | |||
| 469 | <activity android:name="org.gnu.emacs.EmacsActivity" | ||
| 470 | android:launchMode="singleTop" | ||
| 471 | android:windowSoftInputMode="adjustResize" | ||
| 472 | android:exported="true" | ||
| 473 | android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"> | ||
| 474 | <intent-filter> | ||
| 475 | <action android:name="android.intent.action.MAIN" /> | ||
| 476 | <category android:name="android.intent.category.DEFAULT" /> | ||
| 477 | <category android:name="android.intent.category.LAUNCHER" /> | ||
| 478 | </intent-filter> | ||
| 479 | </activity> | ||
| 480 | |||
| 481 | This tells Android to start the activity defined in ``EmacsActivity'' | ||
| 482 | (defined in org/gnu/emacs/EmacsActivity.java), a class extending the | ||
| 483 | Android class ``Activity''. | ||
| 484 | |||
| 485 | To do so, the Android system creates an instance of ``EmacsActivity'' | ||
| 486 | and the window system window associated with it, and eventually calls: | ||
| 487 | |||
| 488 | Activity activity; | ||
| 489 | |||
| 490 | activity.onCreate (...); | ||
| 491 | |||
| 492 | But which ``onCreate'' is really called? | ||
| 493 | It is actually the ``onCreate'' defined in EmacsActivity.java, as | ||
| 494 | it overrides the ``onCreate'' defined in Android's own Activity class: | ||
| 495 | |||
| 496 | @Override | ||
| 497 | public void | ||
| 498 | onCreate (Bundle savedInstanceState) | ||
| 499 | { | ||
| 500 | FrameLayout.LayoutParams params; | ||
| 501 | Intent intent; | ||
| 502 | |||
| 503 | Then, this is what happens step-by-step within the ``onCreate'' | ||
| 504 | function: | ||
| 505 | |||
| 506 | /* See if Emacs should be started with -Q. */ | ||
| 507 | intent = getIntent (); | ||
| 508 | EmacsService.needDashQ | ||
| 509 | = intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q", | ||
| 510 | false); | ||
| 511 | |||
| 512 | Here, Emacs obtains the intent (a request to start a component) which | ||
| 513 | was used to start Emacs, and sets a special flag if it contains a | ||
| 514 | request for Emacs to start with the ``-Q'' command-line argument. | ||
| 515 | |||
| 516 | /* Set the theme to one without a title bar. */ | ||
| 517 | |||
| 518 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) | ||
| 519 | setTheme (android.R.style.Theme_DeviceDefault_NoActionBar); | ||
| 520 | else | ||
| 521 | setTheme (android.R.style.Theme_NoTitleBar); | ||
| 522 | |||
| 523 | Next, Emacs sets an appropriate theme for the activity's associated | ||
| 524 | window decorations. | ||
| 525 | |||
| 526 | params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT, | ||
| 527 | LayoutParams.MATCH_PARENT); | ||
| 528 | |||
| 529 | /* Make the frame layout. */ | ||
| 530 | layout = new FrameLayout (this); | ||
| 531 | layout.setLayoutParams (params); | ||
| 532 | |||
| 533 | /* Set it as the content view. */ | ||
| 534 | setContentView (layout); | ||
| 535 | |||
| 536 | Then, Emacs creates a ``FrameLayout'', a widget that holds a single | ||
| 537 | other widget, and makes it the activity's ``content view''. | ||
| 538 | |||
| 539 | The activity itself is a ``FrameLayout'', so the ``layout parameters'' | ||
| 540 | here apply to the FrameLayout itself, and not its children. | ||
| 541 | |||
| 542 | /* Maybe start the Emacs service if necessary. */ | ||
| 543 | EmacsService.startEmacsService (this); | ||
| 544 | |||
| 545 | And after that, Emacs calls the static function ``startEmacsService'', | ||
| 546 | defined in the class ``EmacsService''. This starts the Emacs service | ||
| 547 | component if necessary. | ||
| 548 | |||
| 549 | /* Add this activity to the list of available activities. */ | ||
| 550 | EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this); | ||
| 551 | |||
| 552 | super.onCreate (savedInstanceState); | ||
| 553 | |||
| 554 | Finally, Emacs registers that this activity is now ready to receive | ||
| 555 | top-level frames (windows) created from Lisp. | ||
| 556 | |||
| 557 | Activities come and go, but Emacs has to stay running in the mean | ||
| 558 | time. Thus, Emacs also defines a ``service'', which is a long-running | ||
| 559 | component that the Android system allows to run in the background. | ||
| 560 | |||
| 561 | Let us go back and review the definition of ``startEmacsService'': | ||
| 562 | |||
| 563 | public static void | ||
| 564 | startEmacsService (Context context) | ||
| 565 | { | ||
| 566 | if (EmacsService.SERVICE == null) | ||
| 567 | { | ||
| 568 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) | ||
| 569 | /* Start the Emacs service now. */ | ||
| 570 | context.startService (new Intent (context, | ||
| 571 | EmacsService.class)); | ||
| 572 | else | ||
| 573 | /* Display the permanant notification and start Emacs as a | ||
| 574 | foreground service. */ | ||
| 575 | context.startForegroundService (new Intent (context, | ||
| 576 | EmacsService.class)); | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 | If ``EmacsService.SERVICE'' does not yet exist, what this does is to | ||
| 581 | tell the ``context'' (the equivalent of an Xlib Display *) to start a | ||
| 582 | service defined by the class ``EmacsService''. Eventually, this | ||
| 583 | results in ``EmacsService.onCreate'' being called: | ||
| 584 | |||
| 585 | @Override | ||
| 586 | public void | ||
| 587 | onCreate () | ||
| 588 | { | ||
| 589 | AssetManager manager; | ||
| 590 | Context app_context; | ||
| 591 | String filesDir, libDir, cacheDir, classPath; | ||
| 592 | double pixelDensityX; | ||
| 593 | double pixelDensityY; | ||
| 594 | |||
| 595 | Here is what this function does, step-by-step: | ||
| 596 | |||
| 597 | SERVICE = this; | ||
| 598 | |||
| 599 | First, it sets the special static variable ``SERVICE'' to ``this'', | ||
| 600 | which is a pointer to the ``EmacsService' object that was created. | ||
| 601 | |||
| 602 | handler = new Handler (Looper.getMainLooper ()); | ||
| 603 | |||
| 604 | Next, it creates a ``Handler'' object for the ``main looper''. | ||
| 605 | This is a helper structure which allows executing code on the Android | ||
| 606 | user interface thread. | ||
| 607 | |||
| 608 | manager = getAssets (); | ||
| 609 | app_context = getApplicationContext (); | ||
| 610 | metrics = getResources ().getDisplayMetrics (); | ||
| 611 | pixelDensityX = metrics.xdpi; | ||
| 612 | pixelDensityY = metrics.ydpi; | ||
| 613 | |||
| 614 | Finally, it obtains: | ||
| 615 | |||
| 616 | - the asset manager, which is used to retrieve assets packaged | ||
| 617 | into the Emacs application package. | ||
| 618 | |||
| 619 | - the application context, used to obtain application specific | ||
| 620 | information. | ||
| 621 | |||
| 622 | - the display metrics, and from them, the X and Y densities in dots | ||
| 623 | per inch. | ||
| 624 | |||
| 625 | Then, inside a ``try'' block: | ||
| 626 | |||
| 627 | try | ||
| 628 | { | ||
| 629 | /* Configure Emacs with the asset manager and other necessary | ||
| 630 | parameters. */ | ||
| 631 | filesDir = app_context.getFilesDir ().getCanonicalPath (); | ||
| 632 | libDir = getLibraryDirectory (); | ||
| 633 | cacheDir = app_context.getCacheDir ().getCanonicalPath (); | ||
| 634 | |||
| 635 | It obtains the names of the Emacs home, shared library, and temporary | ||
| 636 | file directories. | ||
| 637 | |||
| 638 | /* Now provide this application's apk file, so a recursive | ||
| 639 | invocation of app_process (through android-emacs) can | ||
| 640 | find EmacsNoninteractive. */ | ||
| 641 | classPath = getApkFile (); | ||
| 642 | |||
| 643 | The name of the Emacs application package. | ||
| 644 | |||
| 645 | Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir | ||
| 646 | + ", libDir = " + libDir + ", and classPath = " + classPath); | ||
| 647 | |||
| 648 | Prints a debug message to the Android system log with this | ||
| 649 | information. | ||
| 650 | |||
| 651 | EmacsNative.setEmacsParams (manager, filesDir, libDir, | ||
| 652 | cacheDir, (float) pixelDensityX, | ||
| 653 | (float) pixelDensityY, | ||
| 654 | classPath, this); | ||
| 655 | |||
| 656 | And calls the native function ``setEmacsParams'' (defined in | ||
| 657 | android.c) to configure Emacs with this information. | ||
| 658 | |||
| 659 | /* Start the thread that runs Emacs. */ | ||
| 660 | thread = new EmacsThread (this, needDashQ); | ||
| 661 | thread.start (); | ||
| 662 | |||
| 663 | Then, it allocates an ``EmacsThread'' object, and starts that thread. | ||
| 664 | Inside that thread is where Emacs's C code runs. | ||
| 665 | |||
| 666 | } | ||
| 667 | catch (IOException exception) | ||
| 668 | { | ||
| 669 | EmacsNative.emacsAbort (); | ||
| 670 | return; | ||
| 671 | |||
| 672 | And here is the purpose of the ``try'' block. Functions related to | ||
| 673 | file names in Java will signal errors of various types upon failure. | ||
| 674 | |||
| 675 | This ``catch'' block means that the Java virtual machine will abort | ||
| 676 | execution of the contents of the ``try'' block as soon as an error of | ||
| 677 | type ``IOException'' is encountered, and begin executing the contents | ||
| 678 | of the ``catch'' block. | ||
| 679 | |||
| 680 | Any failure of that type here is a crash, and | ||
| 681 | ``EmacsNative.emacsAbort'' is called to quickly abort the process to | ||
| 682 | get a useful backtrace. | ||
| 683 | } | ||
| 684 | } | ||
| 685 | |||
| 686 | Now, let us look at the definition of the class ``EmacsThread'', found | ||
| 687 | in org/gnu/emacs/EmacsThread.java: | ||
| 688 | |||
| 689 | public class EmacsThread extends Thread | ||
| 690 | { | ||
| 691 | /* Whether or not Emacs should be started -Q. */ | ||
| 692 | private boolean startDashQ; | ||
| 693 | |||
| 694 | public | ||
| 695 | EmacsThread (EmacsService service, boolean startDashQ) | ||
| 696 | { | ||
| 697 | super ("Emacs main thread"); | ||
| 698 | this.startDashQ = startDashQ; | ||
| 699 | } | ||
| 700 | |||
| 701 | @Override | ||
| 702 | public void | ||
| 703 | run () | ||
| 704 | { | ||
| 705 | String args[]; | ||
| 706 | |||
| 707 | if (!startDashQ) | ||
| 708 | args = new String[] { "libandroid-emacs.so", }; | ||
| 709 | else | ||
| 710 | args = new String[] { "libandroid-emacs.so", "-Q", }; | ||
| 711 | |||
| 712 | /* Run the native code now. */ | ||
| 713 | EmacsNative.initEmacs (args, EmacsApplication.dumpFileName); | ||
| 714 | } | ||
| 715 | }; | ||
| 716 | |||
| 717 | The class itself defines a single field, ``startDashQ'', a constructor | ||
| 718 | with an unused argument of the type ``EmacsService'' (which is useful | ||
| 719 | while debugging) and a flag ``startDashQ'', and a single function | ||
| 720 | ``run'', overriding the same function in the class ``Thread''. | ||
| 721 | |||
| 722 | When ``thread.start'' is called, the Java virtual machine creates a | ||
| 723 | new thread, and then calls the function ``run'' within that thread. | ||
| 724 | |||
| 725 | This function then computes a suitable argument vector, and calls | ||
| 726 | ``EmacsNative.initEmacs'' (defined in android.c), which then calls a | ||
| 727 | modified version of the regular Emacs ``main'' function. | ||
| 728 | |||
| 729 | At that point, Emacs initialization proceeds as usual: | ||
| 730 | Vinitial_window_system is set, loadup.el calls `normal-top-level', | ||
| 731 | which calls `command-line', and finally | ||
| 732 | `window-system-initialization', which initializes the `android' | ||
| 733 | terminal interface as usual. | ||
| 734 | |||
| 735 | What happens here is the same as on other platforms. Now, here is | ||
| 736 | what happens when the initial frame is created: Fx_create_frame calls | ||
| 737 | `android_create_frame_window' to create a top level window: | ||
| 738 | |||
| 739 | static void | ||
| 740 | android_create_frame_window (struct frame *f) | ||
| 741 | { | ||
| 742 | struct android_set_window_attributes attributes; | ||
| 743 | enum android_window_value_mask attribute_mask; | ||
| 744 | |||
| 745 | attributes.background_pixel = FRAME_BACKGROUND_PIXEL (f); | ||
| 746 | attribute_mask = ANDROID_CW_BACK_PIXEL; | ||
| 747 | |||
| 748 | block_input (); | ||
| 749 | FRAME_ANDROID_WINDOW (f) | ||
| 750 | = android_create_window (FRAME_DISPLAY_INFO (f)->root_window, | ||
| 751 | f->left_pos, | ||
| 752 | f->top_pos, | ||
| 753 | FRAME_PIXEL_WIDTH (f), | ||
| 754 | FRAME_PIXEL_HEIGHT (f), | ||
| 755 | attribute_mask, &attributes); | ||
| 756 | unblock_input (); | ||
| 757 | } | ||
| 758 | |||
| 759 | This calls the function `android_create_window' with some arguments | ||
| 760 | whose meanings are identical to the arguments to `XCreateWindow'. | ||
| 761 | |||
| 762 | Here is the definition of `android_create_window', in android.c: | ||
| 763 | |||
| 764 | android_window | ||
| 765 | android_create_window (android_window parent, int x, int y, | ||
| 766 | int width, int height, | ||
| 767 | enum android_window_value_mask value_mask, | ||
| 768 | struct android_set_window_attributes *attrs) | ||
| 769 | { | ||
| 770 | static jclass class; | ||
| 771 | static jmethodID constructor; | ||
| 772 | jobject object, parent_object, old; | ||
| 773 | android_window window; | ||
| 774 | android_handle prev_max_handle; | ||
| 775 | bool override_redirect; | ||
| 776 | |||
| 777 | What does it do? First, some context: | ||
| 778 | |||
| 779 | At any time, there can be at most 65535 Java objects referred to by | ||
| 780 | the rest of Emacs through the Java native interface. Each such object | ||
| 781 | is assigned a ``handle'' (similar to an XID on X) and given a unique | ||
| 782 | type. The function `android_resolve_handle' returns the JNI `jobject' | ||
| 783 | associated with a given handle. | ||
| 784 | |||
| 785 | parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW); | ||
| 786 | |||
| 787 | Here, it is being used to look up the `jobject' associated with the | ||
| 788 | `parent' handle. | ||
| 789 | |||
| 790 | prev_max_handle = max_handle; | ||
| 791 | window = android_alloc_id (); | ||
| 792 | |||
| 793 | Next, `max_handle' is saved, and a new handle is allocated for | ||
| 794 | `window'. | ||
| 795 | |||
| 796 | if (!window) | ||
| 797 | error ("Out of window handles!"); | ||
| 798 | |||
| 799 | An error is signalled if Emacs runs out of available handles. | ||
| 800 | |||
| 801 | if (!class) | ||
| 802 | { | ||
| 803 | class = (*android_java_env)->FindClass (android_java_env, | ||
| 804 | "org/gnu/emacs/EmacsWindow"); | ||
| 805 | assert (class != NULL); | ||
| 806 | |||
| 807 | Then, if this initialization has not yet been completed, Emacs | ||
| 808 | proceeds to find the Java class named ``EmacsWindow''. | ||
| 809 | |||
| 810 | constructor | ||
| 811 | = (*android_java_env)->GetMethodID (android_java_env, class, "<init>", | ||
| 812 | "(SLorg/gnu/emacs/EmacsWindow;" | ||
| 813 | "IIIIZ)V"); | ||
| 814 | assert (constructor != NULL); | ||
| 815 | |||
| 816 | And it tries to look up the constructor, which should take seven | ||
| 817 | arguments: | ||
| 818 | |||
| 819 | S - a short. (the handle ID) | ||
| 820 | Lorg/gnu/Emacs/EmacsWindow; - an instance of the EmacsWindow | ||
| 821 | class. (the parent) | ||
| 822 | IIII - four ints. (the window geometry.) | ||
| 823 | Z - a boolean. (whether or not the | ||
| 824 | window is override-redirect; see | ||
| 825 | XChangeWindowAttributes.) | ||
| 826 | |||
| 827 | old = class; | ||
| 828 | class = (*android_java_env)->NewGlobalRef (android_java_env, class); | ||
| 829 | (*android_java_env)->ExceptionClear (android_java_env); | ||
| 830 | ANDROID_DELETE_LOCAL_REF (old); | ||
| 831 | |||
| 832 | Next, it saves a global reference to the class and deletes the local | ||
| 833 | reference. Global references will never be deallocated by the Java | ||
| 834 | virtual machine as long as they still exist. | ||
| 835 | |||
| 836 | if (!class) | ||
| 837 | memory_full (0); | ||
| 838 | } | ||
| 839 | |||
| 840 | /* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window | ||
| 841 | creation time. */ | ||
| 842 | override_redirect = ((value_mask | ||
| 843 | & ANDROID_CW_OVERRIDE_REDIRECT) | ||
| 844 | && attrs->override_redirect); | ||
| 845 | |||
| 846 | object = (*android_java_env)->NewObject (android_java_env, class, | ||
| 847 | constructor, (jshort) window, | ||
| 848 | parent_object, (jint) x, (jint) y, | ||
| 849 | (jint) width, (jint) height, | ||
| 850 | (jboolean) override_redirect); | ||
| 851 | |||
| 852 | Then, it creates an instance of the ``EmacsWindow'' class with the | ||
| 853 | appropriate arguments and previously determined constructor. | ||
| 854 | |||
| 855 | if (!object) | ||
| 856 | { | ||
| 857 | (*android_java_env)->ExceptionClear (android_java_env); | ||
| 858 | |||
| 859 | max_handle = prev_max_handle; | ||
| 860 | memory_full (0); | ||
| 861 | |||
| 862 | If creating the object fails, Emacs clears the ``pending exception'' | ||
| 863 | and signals that it is out of memory. | ||
| 864 | } | ||
| 865 | |||
| 866 | android_handles[window].type = ANDROID_HANDLE_WINDOW; | ||
| 867 | android_handles[window].handle | ||
| 868 | = (*android_java_env)->NewGlobalRef (android_java_env, | ||
| 869 | object); | ||
| 870 | (*android_java_env)->ExceptionClear (android_java_env); | ||
| 871 | ANDROID_DELETE_LOCAL_REF (object); | ||
| 872 | |||
| 873 | Otherwise, it associates a new global reference to the object with the | ||
| 874 | handle, and deletes the local reference returned from the JNI | ||
| 875 | NewObject function. | ||
| 876 | |||
| 877 | if (!android_handles[window].handle) | ||
| 878 | memory_full (0); | ||
| 879 | |||
| 880 | If allocating the global reference fails, Emacs signals that it is out | ||
| 881 | of memory. | ||
| 882 | |||
| 883 | android_change_window_attributes (window, value_mask, attrs); | ||
| 884 | return window; | ||
| 885 | |||
| 886 | Otherwise, it applies the specified window attributes and returns the | ||
| 887 | handle of the new window. | ||
| 888 | } | ||
| 889 | |||
| 890 | |||
| 891 | |||
| 892 | DRAWABLES, CURSORS AND HANDLES | ||
| 893 | |||
| 894 | Each widget created by Emacs corresponds to a single ``window'', which | ||
| 895 | has its own backing store. This arrangement is quite similar to X. | ||
| 896 | |||
| 897 | C code does not directly refer to the EmacsView widgets that implement | ||
| 898 | the UI logic behind windows. Instead, its handles refer to | ||
| 899 | EmacsWindow structures, which contain the state necessary to interact | ||
| 900 | with the widgets in an orderly and synchronized manner. | ||
| 901 | |||
| 902 | Like X, both pixmaps and windows are drawable resources, and the same | ||
| 903 | graphics operations can be applied to both. Thus, a separate | ||
| 904 | EmacsPixmap structure is used to wrap around Android Bitmap resources, | ||
| 905 | and the Java-level graphics operation functions are capable of | ||
| 906 | operating on them both. | ||
| 907 | |||
| 908 | Finally, graphics contexts are maintained on both the C and Java | ||
| 909 | levels; the C state recorded in `struct android_gc' is kept in sync | ||
| 910 | with the Java state in the GContext handle's corresponding EmacsGC | ||
| 911 | structure, and cursors are used through handles that refer to | ||
| 912 | EmacsCursor structures that hold system PointerIcons. | ||
| 913 | |||
| 914 | In all cases, the interfaces provided are identical to X. | ||
| 915 | |||
| 916 | |||
| 917 | |||
| 918 | EVENT LOOP | ||
| 919 | |||
| 920 | In a typical Android application, the event loop is managed by the | ||
| 921 | operating system, and callbacks (implemented through overriding | ||
| 922 | separate functions in widgets) are run by the event loop wherever | ||
| 923 | necessary. The thread which runs the event loop is also the only | ||
| 924 | thread capable of creating and manipulating widgets and activities, | ||
| 925 | and is referred to as the ``UI thread''. | ||
| 926 | |||
| 927 | These callbacks are used by Emacs to write representations of X-like | ||
| 928 | events to a separate event queue, which are then read from Emacs's own | ||
| 929 | event loop running in a separate thread. This is accomplished through | ||
| 930 | replacing `select' by a function which waits for the event queue to be | ||
| 931 | occupied, in addition to any file descriptors that `select' would | ||
| 932 | normally wait for. | ||
| 933 | |||
| 934 | Conversely, Emacs's event loop sometimes needs to send events to the | ||
| 935 | UI thread. These events are implemented as tiny fragments of code, | ||
| 936 | which are run as they are received by the main thread. | ||
| 937 | |||
| 938 | A typical example is `displayToast', which is implemented in | ||
| 939 | EmacsService.java: | ||
| 940 | |||
| 941 | public void | ||
| 942 | displayToast (final String string) | ||
| 943 | { | ||
| 944 | runOnUiThread (new Runnable () { | ||
| 945 | @Override | ||
| 946 | public void | ||
| 947 | run () | ||
| 948 | { | ||
| 949 | Toast toast; | ||
| 950 | |||
| 951 | toast = Toast.makeText (getApplicationContext (), | ||
| 952 | string, Toast.LENGTH_SHORT); | ||
| 953 | toast.show (); | ||
| 954 | } | ||
| 955 | }); | ||
| 956 | } | ||
| 957 | |||
| 958 | Here, the variable `string' is used by a nested function. This nested | ||
| 959 | function contains a copy of that variable, and is run on the main | ||
| 960 | thread using the function `runOnUiThread', in order to display a short | ||
| 961 | status message on the display. | ||
| 962 | |||
| 963 | When Emacs needs to wait for the nested function to finish, it uses a | ||
| 964 | mechanism implemented in `syncRunnable'. This mechanism first calls a | ||
| 965 | deadlock avoidance mechanism, then runs a nested function on the UI | ||
| 966 | thread, which is expected to signal itself as a condition variable | ||
| 967 | upon completion. It is typically used to allocate resources that can | ||
| 968 | only be allocated from the UI thread, or to obtain non-thread-safe | ||
| 969 | information. The following function is an example; it returns a new | ||
| 970 | EmacsView widget corresponding to the provided window: | ||
| 971 | |||
| 972 | public EmacsView | ||
| 973 | getEmacsView (final EmacsWindow window, final int visibility, | ||
| 974 | final boolean isFocusedByDefault) | ||
| 975 | { | ||
| 976 | Runnable runnable; | ||
| 977 | final EmacsHolder<EmacsView> view; | ||
| 978 | |||
| 979 | view = new EmacsHolder<EmacsView> (); | ||
| 980 | |||
| 981 | runnable = new Runnable () { | ||
| 982 | public void | ||
| 983 | run () | ||
| 984 | { | ||
| 985 | synchronized (this) | ||
| 986 | { | ||
| 987 | view.thing = new EmacsView (window); | ||
| 988 | view.thing.setVisibility (visibility); | ||
| 989 | |||
| 990 | /* The following function is only present on Android 26 | ||
| 991 | or later. */ | ||
| 992 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) | ||
| 993 | view.thing.setFocusedByDefault (isFocusedByDefault); | ||
| 994 | |||
| 995 | notify (); | ||
| 996 | } | ||
| 997 | } | ||
| 998 | }; | ||
| 999 | |||
| 1000 | syncRunnable (runnable); | ||
| 1001 | return view.thing; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | As no value can be directly returned from the nested function, a | ||
| 1005 | separate container object is used to hold the result after the | ||
| 1006 | function finishes execution. Note the type name inside the angle | ||
| 1007 | brackets: this type is substituted into the class definition as it is | ||
| 1008 | used; a definition such as: | ||
| 1009 | |||
| 1010 | public class Foo<T> | ||
| 1011 | { | ||
| 1012 | T bar; | ||
| 1013 | }; | ||
| 1014 | |||
| 1015 | can not be used alone: | ||
| 1016 | |||
| 1017 | Foo holder; /* Error! */ | ||
| 1018 | |||
| 1019 | but must have a type specified: | ||
| 1020 | |||
| 1021 | Foo<Object> holder; | ||
| 1022 | |||
| 1023 | in which case the effective definition is: | ||
| 1024 | |||
| 1025 | public class Foo | ||
| 1026 | { | ||
| 1027 | Object bar; | ||
| 1028 | }; | ||
| 1029 | |||
| 1030 | |||
| 1031 | |||
| 1032 | COMPATIBILITY | ||
| 1033 | |||
| 1034 | There are three variables set within every Android application that | ||
| 1035 | extert influence over the set of Android systems it supports, and the | ||
| 1036 | measures it must take to function faithfully on each of those systems: | ||
| 1037 | the minimum API level, compile SDK version and target API level. | ||
| 1038 | |||
| 1039 | The minimum API level is the earliest version of Android that is | ||
| 1040 | permitted to install and run the application. For Emacs, this is | ||
| 1041 | established by detecting the __ANDROID_API__ preprocessor macro | ||
| 1042 | defined within the Android C compiler. | ||
| 1043 | |||
| 1044 | Before Java code executes any Android API calls that are not present | ||
| 1045 | within Android 2.2 (API level 8), the lowest API level supported by | ||
| 1046 | Emacs as a whole, it must first check the value of the: | ||
| 1047 | |||
| 1048 | Build.VERSION.SDK_INT | ||
| 1049 | |||
| 1050 | variable, which is always set to the API level of the system Emacs is | ||
| 1051 | presently installed within. For example, before calling | ||
| 1052 | `dispatchKeyEventFromInputMethod', a function absent from Android 6.0 | ||
| 1053 | (API level 23) or earlier, check: | ||
| 1054 | |||
| 1055 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) | ||
| 1056 | view.imManager.dispatchKeyEventFromInputMethod (view, key); | ||
| 1057 | else | ||
| 1058 | { | ||
| 1059 | |||
| 1060 | where `N' is a constant defined to 24. | ||
| 1061 | |||
| 1062 | The compile SDK version is the version of the Android SDK headers Java | ||
| 1063 | code is compiled against. Because Java does not provide conditional | ||
| 1064 | compilation constructs, Emacs can't be compiled with any version of | ||
| 1065 | these headers other than the version mentioned in `java/INSTALL', but | ||
| 1066 | the headers used do not affect the set of supported systems provided | ||
| 1067 | that the version checks illustrated above are performed where | ||
| 1068 | necessary. | ||
| 1069 | |||
| 1070 | The target API level is a number within java/AndroidManifest.xml.in | ||
| 1071 | the system refers to when deciding whether to enable | ||
| 1072 | backwards-incompatible modifications to the behavior of various system | ||
| 1073 | APIs. For any given Android version, backwards incompatible changes | ||
| 1074 | in that version will be disabled for applications whose target API | ||
| 1075 | levels don't exceed its own. | ||
| 1076 | |||
| 1077 | The target API should nevertheless be updated to match every major | ||
| 1078 | Android update, as Google has stated their intentions to prohibit | ||
| 1079 | users from installing applications targeting ``out-of-date'' versions | ||
| 1080 | of Android, though this threat has hitherto been made good on. | ||
| 1081 | |||
| 1082 | |||
| 1083 | |||
| 1084 | This file is part of GNU Emacs. | ||
| 1085 | |||
| 1086 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 1087 | it under the terms of the GNU General Public License as published by | ||
| 1088 | the Free Software Foundation, either version 3 of the License, or | ||
| 1089 | (at your option) any later version. | ||
| 1090 | |||
| 1091 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 1092 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 1093 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 1094 | GNU General Public License for more details. | ||
| 1095 | |||
| 1096 | You should have received a copy of the GNU General Public License | ||
| 1097 | along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. | ||