1098 lines
30 KiB
Plaintext
1098 lines
30 KiB
Plaintext
Installation instructions for Android
|
||
Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||
See the end of the file for license conditions.
|
||
|
||
|
||
|
||
OVERVIEW OF JAVA
|
||
|
||
Emacs developers do not know Java, and there is no reason they should
|
||
have to. Thus, the code in this directory is confined to what is
|
||
strictly necessary to support Emacs, and only uses a subset of Java
|
||
written in a way that is easily understandable to C programmers.
|
||
|
||
Java is required because the entire Android runtime is based around
|
||
Java, and there is no way to write an Android program which runs
|
||
without Java.
|
||
|
||
This text exists to prime other Emacs developers, already familiar with
|
||
C, on the basic architecture of the Android port, and to teach them
|
||
how to read and write the Java code found in this directory.
|
||
|
||
Java is an object oriented language with automatic memory management
|
||
compiled down to bytecode, which is then subject to interpretation by
|
||
a Java virtual machine.
|
||
|
||
What that means, is that:
|
||
|
||
struct emacs_window
|
||
{
|
||
int some_fields;
|
||
int of_emacs_window;
|
||
};
|
||
|
||
static void
|
||
do_something_with_emacs_window (struct emacs_window *a, int n)
|
||
{
|
||
a->some_fields = a->of_emacs_window + n;
|
||
}
|
||
|
||
would be written:
|
||
|
||
public class EmacsWindow
|
||
{
|
||
public int someFields;
|
||
public int ofEmacsWindow;
|
||
|
||
public void
|
||
doSomething (int n)
|
||
{
|
||
someFields = ofEmacsWindow + n;
|
||
}
|
||
}
|
||
|
||
and instead of doing:
|
||
|
||
do_something_with_emacs_window (my_window, 1);
|
||
|
||
you say:
|
||
|
||
myWindow.doSomething (1);
|
||
|
||
In addition to functions associated with an object of a given class
|
||
(such as EmacsWindow), Java also has two other kinds of functions.
|
||
|
||
The first are so-called ``static'' functions (the static means
|
||
something entirely different from what it does in C.)
|
||
|
||
A static function, while still having to be defined within a class,
|
||
can be called without any object. Instead of the object, you write
|
||
the name of the Java class within which it is defined. For example,
|
||
the following C code:
|
||
|
||
int
|
||
multiply_a_with_b_and_then_add_c (int a, int b, int c)
|
||
{
|
||
return a * b + c;
|
||
}
|
||
|
||
would be:
|
||
|
||
public class EmacsSomething
|
||
{
|
||
public static int
|
||
multiplyAWithBAndThenAddC (int a, int b, int c)
|
||
{
|
||
return a * b + c;
|
||
}
|
||
};
|
||
|
||
Then, instead of calling:
|
||
|
||
int foo;
|
||
|
||
foo = multiply_a_with_b_then_add_c (1, 2, 3);
|
||
|
||
you say:
|
||
|
||
int foo;
|
||
|
||
foo = EmacsSomething.multiplyAWithBAndThenAddC (1, 2, 3);
|
||
|
||
In Java, ``static'' does not mean that the function is only used
|
||
within its compilation unit! Instead, the ``private'' qualifier is
|
||
used to mean more or less the same thing:
|
||
|
||
static void
|
||
this_procedure_is_only_used_within_this_file (void)
|
||
{
|
||
do_something ();
|
||
}
|
||
|
||
becomes
|
||
|
||
public class EmacsSomething
|
||
{
|
||
private static void
|
||
thisProcedureIsOnlyUsedWithinThisClass ()
|
||
{
|
||
|
||
}
|
||
}
|
||
|
||
the other kind are called ``constructors''. They are functions that
|
||
must be called to allocate memory to hold a class:
|
||
|
||
public class EmacsFoo
|
||
{
|
||
int bar;
|
||
|
||
public
|
||
EmacsFoo (int tokenA, int tokenB)
|
||
{
|
||
bar = tokenA + tokenB;
|
||
}
|
||
}
|
||
|
||
now, the following statement:
|
||
|
||
EmacsFoo foo;
|
||
|
||
foo = new EmacsFoo (1, 2);
|
||
|
||
becomes more or less equivalent to the following C code:
|
||
|
||
struct emacs_foo
|
||
{
|
||
int bar;
|
||
};
|
||
|
||
struct emacs_foo *
|
||
make_emacs_foo (int token_a, int token_b)
|
||
{
|
||
struct emacs_foo *foo;
|
||
|
||
foo = xmalloc (sizeof *foo);
|
||
foo->bar = token_a + token_b;
|
||
|
||
return foo;
|
||
}
|
||
|
||
/* ... */
|
||
|
||
struct emacs_foo *foo;
|
||
|
||
foo = make_emacs_foo (1, 2);
|
||
|
||
A class may have any number of constructors, or no constructors at
|
||
all, in which case the compiler inserts an empty constructor.
|
||
|
||
|
||
|
||
Sometimes, you will see Java code that looks like this:
|
||
|
||
allFiles = filesDirectory.listFiles (new FileFilter () {
|
||
@Override
|
||
public boolean
|
||
accept (File file)
|
||
{
|
||
return (!file.isDirectory ()
|
||
&& file.getName ().endsWith (".pdmp"));
|
||
}
|
||
});
|
||
|
||
This is Java's version of GCC's nested function extension. The major
|
||
difference is that the nested function may still be called even after
|
||
it goes out of scope, and always retains a reference to the class and
|
||
local variables around where it was called.
|
||
|
||
Being an object-oriented language, Java also allows defining that a
|
||
class ``extends'' another class. The following C code:
|
||
|
||
struct a
|
||
{
|
||
long thirty_two;
|
||
};
|
||
|
||
struct b
|
||
{
|
||
struct a a;
|
||
long long sixty_four;
|
||
};
|
||
|
||
extern void do_something (struct a *);
|
||
|
||
void
|
||
my_function (struct b *b)
|
||
{
|
||
do_something (&b->a);
|
||
}
|
||
|
||
is roughly equivalent to the following Java code, split into two
|
||
files:
|
||
|
||
A.java
|
||
|
||
public class A
|
||
{
|
||
int thirtyTwo;
|
||
|
||
public void
|
||
doSomething ()
|
||
{
|
||
etcEtcEtc ();
|
||
}
|
||
};
|
||
|
||
B.java
|
||
|
||
public class B extends A
|
||
{
|
||
long sixty_four;
|
||
|
||
public static void
|
||
myFunction (B b)
|
||
{
|
||
b.doSomething ();
|
||
}
|
||
}
|
||
|
||
the Java runtime has transformed the call to ``b.doSomething'' to
|
||
``((A) b).doSomething''.
|
||
|
||
However, Java also allows overriding this behavior, by specifying the
|
||
@Override keyword:
|
||
|
||
public class B extends A
|
||
{
|
||
long sixty_four;
|
||
|
||
@Override
|
||
public void
|
||
doSomething ()
|
||
{
|
||
Something.doSomethingTwo ();
|
||
super.doSomething ();
|
||
}
|
||
}
|
||
|
||
now, any call to ``doSomething'' on a ``B'' created using ``new B ()''
|
||
will end up calling ``Something.doSomethingTwo'', before calling back
|
||
to ``A.doSomething''. This override also applies in reverse; that is
|
||
to say, even if you write:
|
||
|
||
((A) b).doSomething ();
|
||
|
||
B's version of doSomething will still be called, if ``b'' was created
|
||
using ``new B ()''.
|
||
|
||
This mechanism is used extensively throughout the Java language and
|
||
Android windowing APIs.
|
||
|
||
Elsewhere, you will encounter Java code that defines arrays:
|
||
|
||
public class EmacsFrobinicator
|
||
{
|
||
public static void
|
||
emacsFrobinicate (int something)
|
||
{
|
||
int[] primesFromSomething;
|
||
|
||
primesFromSomething = new int[numberOfPrimes];
|
||
/* ... */
|
||
}
|
||
}
|
||
|
||
Java arrays are similar to C arrays in that they can not grow. But
|
||
they are very much unlike C arrays in that they are always references
|
||
(as opposed to decaying into pointers in only some situations), and
|
||
contain information about their length.
|
||
|
||
If another function named ``frobinicate1'' takes an array as an
|
||
argument, then it need not take the length of the array.
|
||
|
||
Instead, it may simply iterate over the array like so:
|
||
|
||
int i, k;
|
||
|
||
for (i = 0; i < array.length; ++i)
|
||
{
|
||
k = array[i];
|
||
|
||
Whatever.doSomethingWithK (k);
|
||
}
|
||
|
||
The syntax used to define arrays is also slightly different. As
|
||
arrays are always references, there is no way for you to tell the
|
||
runtime to allocate an array of size N in a structure (class.)
|
||
|
||
Instead, if you need an array of that size, you must declare a field
|
||
with the type of the array, and allocate the array inside the class's
|
||
constructor, like so:
|
||
|
||
public class EmacsArrayContainer
|
||
{
|
||
public int[] myArray;
|
||
|
||
public
|
||
EmacsArrayContainer ()
|
||
{
|
||
myArray = new array[10];
|
||
}
|
||
}
|
||
|
||
while in C, you could just have written:
|
||
|
||
struct emacs_array_container
|
||
{
|
||
int my_array[10];
|
||
};
|
||
|
||
or, possibly even better,
|
||
|
||
typedef int emacs_array_container[10];
|
||
|
||
Alas, Java has no equivalent of `typedef'.
|
||
|
||
Like in C, Java string literals are delimited by double quotes.
|
||
Unlike C, however, strings are not NULL-terminated arrays of
|
||
characters, but a distinct type named ``String''. They store their
|
||
own length, characters in Java's 16-bit ``char'' type, and are capable
|
||
of holding NULL bytes.
|
||
|
||
Instead of writing:
|
||
|
||
wchar_t character;
|
||
extern char *s;
|
||
size_t s;
|
||
|
||
for (/* determine n, s in a loop. */)
|
||
s += mbstowc (&character, s, n);
|
||
|
||
or:
|
||
|
||
const char *byte;
|
||
|
||
for (byte = my_string; *byte; ++byte)
|
||
/* do something with *byte. */;
|
||
|
||
or perhaps even:
|
||
|
||
size_t length, i;
|
||
char foo;
|
||
|
||
length = strlen (my_string);
|
||
|
||
for (i = 0; i < length; ++i)
|
||
foo = my_string[i];
|
||
|
||
you write:
|
||
|
||
char foo;
|
||
int i;
|
||
|
||
for (i = 0; i < myString.length (); ++i)
|
||
foo = myString.charAt (0);
|
||
|
||
Java also has stricter rules on what can be used as a truth value in a
|
||
conditional. While in C, any non-zero value is true, Java requires
|
||
that every truth value be of the boolean type ``boolean''.
|
||
|
||
What this means is that instead of simply writing:
|
||
|
||
if (foo || bar)
|
||
|
||
where foo can either be 1 or 0, and bar can either be NULL or a
|
||
pointer to something, you must explicitly write:
|
||
|
||
if (foo != 0 || bar != null)
|
||
|
||
in Java.
|
||
|
||
JAVA NATIVE INTERFACE
|
||
|
||
Java also provides an interface for C code to interface with Java.
|
||
|
||
C functions exported from a shared library become static Java
|
||
functions within a class, like so:
|
||
|
||
public class EmacsNative
|
||
{
|
||
/* Obtain the fingerprint of this build of Emacs. The fingerprint
|
||
can be used to determine the dump file name. */
|
||
public static native String getFingerprint ();
|
||
|
||
/* Set certain parameters before initializing Emacs.
|
||
|
||
assetManager must be the asset manager associated with the
|
||
context that is loading Emacs. It is saved and remains for the
|
||
remainder the lifetime of the Emacs process.
|
||
|
||
filesDir must be the package's data storage location for the
|
||
current Android user.
|
||
|
||
libDir must be the package's data storage location for native
|
||
libraries. It is used as PATH.
|
||
|
||
cacheDir must be the package's cache directory. It is used as
|
||
the `temporary-file-directory'.
|
||
|
||
pixelDensityX and pixelDensityY are the DPI values that will be
|
||
used by Emacs.
|
||
|
||
classPath must be the classpath of this app_process process, or
|
||
NULL.
|
||
|
||
emacsService must be the EmacsService singleton, or NULL. */
|
||
public static native void setEmacsParams (AssetManager assetManager,
|
||
String filesDir,
|
||
String libDir,
|
||
String cacheDir,
|
||
float pixelDensityX,
|
||
float pixelDensityY,
|
||
String classPath,
|
||
EmacsService emacsService);
|
||
}
|
||
|
||
Where the corresponding C functions are located in android.c, and
|
||
loaded by the special invocation:
|
||
|
||
static
|
||
{
|
||
System.loadLibrary ("emacs");
|
||
};
|
||
|
||
where ``static'' defines a section of code which will be run upon the
|
||
object (containing class) being loaded. This is like:
|
||
|
||
__attribute__ ((constructor))
|
||
|
||
on systems where shared object constructors are supported.
|
||
|
||
See http://docs.oracle.com/en/java/javase/19/docs/specs/jni/intro.html
|
||
for more details.
|
||
|
||
|
||
|
||
OVERVIEW OF ANDROID
|
||
|
||
When the Android system starts an application, it does not actually
|
||
call the application's ``main'' function. It may not even start the
|
||
application's process if one is already running.
|
||
|
||
Instead, Android is organized around components. When the user opens
|
||
the ``Emacs'' icon, the Android system looks up and starts the
|
||
component associated with the ``Emacs'' icon. In this case, the
|
||
component is called an activity, and is declared in
|
||
the AndroidManifest.xml in this directory:
|
||
|
||
<activity android:name="org.gnu.emacs.EmacsActivity"
|
||
android:launchMode="singleTop"
|
||
android:windowSoftInputMode="adjustResize"
|
||
android:exported="true"
|
||
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
|
||
<intent-filter>
|
||
<action android:name="android.intent.action.MAIN" />
|
||
<category android:name="android.intent.category.DEFAULT" />
|
||
<category android:name="android.intent.category.LAUNCHER" />
|
||
</intent-filter>
|
||
</activity>
|
||
|
||
This tells Android to start the activity defined in ``EmacsActivity''
|
||
(defined in org/gnu/emacs/EmacsActivity.java), a class extending the
|
||
Android class ``Activity''.
|
||
|
||
To do so, the Android system creates an instance of ``EmacsActivity''
|
||
and the window system window associated with it, and eventually calls:
|
||
|
||
Activity activity;
|
||
|
||
activity.onCreate (...);
|
||
|
||
But which ``onCreate'' is really called?
|
||
It is actually the ``onCreate'' defined in EmacsActivity.java, as
|
||
it overrides the ``onCreate'' defined in Android's own Activity class:
|
||
|
||
@Override
|
||
public void
|
||
onCreate (Bundle savedInstanceState)
|
||
{
|
||
FrameLayout.LayoutParams params;
|
||
Intent intent;
|
||
|
||
Then, this is what happens step-by-step within the ``onCreate''
|
||
function:
|
||
|
||
/* See if Emacs should be started with -Q. */
|
||
intent = getIntent ();
|
||
EmacsService.needDashQ
|
||
= intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q",
|
||
false);
|
||
|
||
Here, Emacs obtains the intent (a request to start a component) which
|
||
was used to start Emacs, and sets a special flag if it contains a
|
||
request for Emacs to start with the ``-Q'' command-line argument.
|
||
|
||
/* Set the theme to one without a title bar. */
|
||
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||
setTheme (android.R.style.Theme_DeviceDefault_NoActionBar);
|
||
else
|
||
setTheme (android.R.style.Theme_NoTitleBar);
|
||
|
||
Next, Emacs sets an appropriate theme for the activity's associated
|
||
window decorations.
|
||
|
||
params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
|
||
LayoutParams.MATCH_PARENT);
|
||
|
||
/* Make the frame layout. */
|
||
layout = new FrameLayout (this);
|
||
layout.setLayoutParams (params);
|
||
|
||
/* Set it as the content view. */
|
||
setContentView (layout);
|
||
|
||
Then, Emacs creates a ``FrameLayout'', a widget that holds a single
|
||
other widget, and makes it the activity's ``content view''.
|
||
|
||
The activity itself is a ``FrameLayout'', so the ``layout parameters''
|
||
here apply to the FrameLayout itself, and not its children.
|
||
|
||
/* Maybe start the Emacs service if necessary. */
|
||
EmacsService.startEmacsService (this);
|
||
|
||
And after that, Emacs calls the static function ``startEmacsService'',
|
||
defined in the class ``EmacsService''. This starts the Emacs service
|
||
component if necessary.
|
||
|
||
/* Add this activity to the list of available activities. */
|
||
EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
|
||
|
||
super.onCreate (savedInstanceState);
|
||
|
||
Finally, Emacs registers that this activity is now ready to receive
|
||
top-level frames (windows) created from Lisp.
|
||
|
||
Activities come and go, but Emacs has to stay running in the mean
|
||
time. Thus, Emacs also defines a ``service'', which is a long-running
|
||
component that the Android system allows to run in the background.
|
||
|
||
Let us go back and review the definition of ``startEmacsService'':
|
||
|
||
public static void
|
||
startEmacsService (Context context)
|
||
{
|
||
if (EmacsService.SERVICE == null)
|
||
{
|
||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
||
/* Start the Emacs service now. */
|
||
context.startService (new Intent (context,
|
||
EmacsService.class));
|
||
else
|
||
/* Display the permanent notification and start Emacs as a
|
||
foreground service. */
|
||
context.startForegroundService (new Intent (context,
|
||
EmacsService.class));
|
||
}
|
||
}
|
||
|
||
If ``EmacsService.SERVICE'' does not yet exist, what this does is to
|
||
tell the ``context'' (the equivalent of an Xlib Display *) to start a
|
||
service defined by the class ``EmacsService''. Eventually, this
|
||
results in ``EmacsService.onCreate'' being called:
|
||
|
||
@Override
|
||
public void
|
||
onCreate ()
|
||
{
|
||
AssetManager manager;
|
||
Context app_context;
|
||
String filesDir, libDir, cacheDir, classPath;
|
||
double pixelDensityX;
|
||
double pixelDensityY;
|
||
|
||
Here is what this function does, step-by-step:
|
||
|
||
SERVICE = this;
|
||
|
||
First, it sets the special static variable ``SERVICE'' to ``this'',
|
||
which is a pointer to the ``EmacsService' object that was created.
|
||
|
||
handler = new Handler (Looper.getMainLooper ());
|
||
|
||
Next, it creates a ``Handler'' object for the ``main looper''.
|
||
This is a helper structure which allows executing code on the Android
|
||
user interface thread.
|
||
|
||
manager = getAssets ();
|
||
app_context = getApplicationContext ();
|
||
metrics = getResources ().getDisplayMetrics ();
|
||
pixelDensityX = metrics.xdpi;
|
||
pixelDensityY = metrics.ydpi;
|
||
|
||
Finally, it obtains:
|
||
|
||
- the asset manager, which is used to retrieve assets packaged
|
||
into the Emacs application package.
|
||
|
||
- the application context, used to obtain application specific
|
||
information.
|
||
|
||
- the display metrics, and from them, the X and Y densities in dots
|
||
per inch.
|
||
|
||
Then, inside a ``try'' block:
|
||
|
||
try
|
||
{
|
||
/* Configure Emacs with the asset manager and other necessary
|
||
parameters. */
|
||
filesDir = app_context.getFilesDir ().getCanonicalPath ();
|
||
libDir = getLibraryDirectory ();
|
||
cacheDir = app_context.getCacheDir ().getCanonicalPath ();
|
||
|
||
It obtains the names of the Emacs home, shared library, and temporary
|
||
file directories.
|
||
|
||
/* Now provide this application's apk file, so a recursive
|
||
invocation of app_process (through android-emacs) can
|
||
find EmacsNoninteractive. */
|
||
classPath = getApkFile ();
|
||
|
||
The name of the Emacs application package.
|
||
|
||
Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
|
||
+ ", libDir = " + libDir + ", and classPath = " + classPath);
|
||
|
||
Prints a debug message to the Android system log with this
|
||
information.
|
||
|
||
EmacsNative.setEmacsParams (manager, filesDir, libDir,
|
||
cacheDir, (float) pixelDensityX,
|
||
(float) pixelDensityY,
|
||
classPath, this);
|
||
|
||
And calls the native function ``setEmacsParams'' (defined in
|
||
android.c) to configure Emacs with this information.
|
||
|
||
/* Start the thread that runs Emacs. */
|
||
thread = new EmacsThread (this, needDashQ);
|
||
thread.start ();
|
||
|
||
Then, it allocates an ``EmacsThread'' object, and starts that thread.
|
||
Inside that thread is where Emacs's C code runs.
|
||
|
||
}
|
||
catch (IOException exception)
|
||
{
|
||
EmacsNative.emacsAbort ();
|
||
return;
|
||
|
||
And here is the purpose of the ``try'' block. Functions related to
|
||
file names in Java will signal errors of various types upon failure.
|
||
|
||
This ``catch'' block means that the Java virtual machine will abort
|
||
execution of the contents of the ``try'' block as soon as an error of
|
||
type ``IOException'' is encountered, and begin executing the contents
|
||
of the ``catch'' block.
|
||
|
||
Any failure of that type here is a crash, and
|
||
``EmacsNative.emacsAbort'' is called to quickly abort the process to
|
||
get a useful backtrace.
|
||
}
|
||
}
|
||
|
||
Now, let us look at the definition of the class ``EmacsThread'', found
|
||
in org/gnu/emacs/EmacsThread.java:
|
||
|
||
public class EmacsThread extends Thread
|
||
{
|
||
/* Whether or not Emacs should be started -Q. */
|
||
private boolean startDashQ;
|
||
|
||
public
|
||
EmacsThread (EmacsService service, boolean startDashQ)
|
||
{
|
||
super ("Emacs main thread");
|
||
this.startDashQ = startDashQ;
|
||
}
|
||
|
||
@Override
|
||
public void
|
||
run ()
|
||
{
|
||
String args[];
|
||
|
||
if (!startDashQ)
|
||
args = new String[] { "libandroid-emacs.so", };
|
||
else
|
||
args = new String[] { "libandroid-emacs.so", "-Q", };
|
||
|
||
/* Run the native code now. */
|
||
EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
|
||
}
|
||
};
|
||
|
||
The class itself defines a single field, ``startDashQ'', a constructor
|
||
with an unused argument of the type ``EmacsService'' (which is useful
|
||
while debugging) and a flag ``startDashQ'', and a single function
|
||
``run'', overriding the same function in the class ``Thread''.
|
||
|
||
When ``thread.start'' is called, the Java virtual machine creates a
|
||
new thread, and then calls the function ``run'' within that thread.
|
||
|
||
This function then computes a suitable argument vector, and calls
|
||
``EmacsNative.initEmacs'' (defined in android.c), which then calls a
|
||
modified version of the regular Emacs ``main'' function.
|
||
|
||
At that point, Emacs initialization proceeds as usual:
|
||
Vinitial_window_system is set, loadup.el calls `normal-top-level',
|
||
which calls `command-line', and finally
|
||
`window-system-initialization', which initializes the `android'
|
||
terminal interface as usual.
|
||
|
||
What happens here is the same as on other platforms. Now, here is
|
||
what happens when the initial frame is created: Fx_create_frame calls
|
||
`android_create_frame_window' to create a top level window:
|
||
|
||
static void
|
||
android_create_frame_window (struct frame *f)
|
||
{
|
||
struct android_set_window_attributes attributes;
|
||
enum android_window_value_mask attribute_mask;
|
||
|
||
attributes.background_pixel = FRAME_BACKGROUND_PIXEL (f);
|
||
attribute_mask = ANDROID_CW_BACK_PIXEL;
|
||
|
||
block_input ();
|
||
FRAME_ANDROID_WINDOW (f)
|
||
= android_create_window (FRAME_DISPLAY_INFO (f)->root_window,
|
||
f->left_pos,
|
||
f->top_pos,
|
||
FRAME_PIXEL_WIDTH (f),
|
||
FRAME_PIXEL_HEIGHT (f),
|
||
attribute_mask, &attributes);
|
||
unblock_input ();
|
||
}
|
||
|
||
This calls the function `android_create_window' with some arguments
|
||
whose meanings are identical to the arguments to `XCreateWindow'.
|
||
|
||
Here is the definition of `android_create_window', in android.c:
|
||
|
||
android_window
|
||
android_create_window (android_window parent, int x, int y,
|
||
int width, int height,
|
||
enum android_window_value_mask value_mask,
|
||
struct android_set_window_attributes *attrs)
|
||
{
|
||
static jclass class;
|
||
static jmethodID constructor;
|
||
jobject object, parent_object, old;
|
||
android_window window;
|
||
android_handle prev_max_handle;
|
||
bool override_redirect;
|
||
|
||
What does it do? First, some context:
|
||
|
||
At any time, there can be at most 65535 Java objects referred to by
|
||
the rest of Emacs through the Java native interface. Each such object
|
||
is assigned a ``handle'' (similar to an XID on X) and given a unique
|
||
type. The function `android_resolve_handle' returns the JNI `jobject'
|
||
associated with a given handle.
|
||
|
||
parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW);
|
||
|
||
Here, it is being used to look up the `jobject' associated with the
|
||
`parent' handle.
|
||
|
||
prev_max_handle = max_handle;
|
||
window = android_alloc_id ();
|
||
|
||
Next, `max_handle' is saved, and a new handle is allocated for
|
||
`window'.
|
||
|
||
if (!window)
|
||
error ("Out of window handles!");
|
||
|
||
An error is signaled if Emacs runs out of available handles.
|
||
|
||
if (!class)
|
||
{
|
||
class = (*android_java_env)->FindClass (android_java_env,
|
||
"org/gnu/emacs/EmacsWindow");
|
||
assert (class != NULL);
|
||
|
||
Then, if this initialization has not yet been completed, Emacs
|
||
proceeds to find the Java class named ``EmacsWindow''.
|
||
|
||
constructor
|
||
= (*android_java_env)->GetMethodID (android_java_env, class, "<init>",
|
||
"(SLorg/gnu/emacs/EmacsWindow;"
|
||
"IIIIZ)V");
|
||
assert (constructor != NULL);
|
||
|
||
And it tries to look up the constructor, which should take seven
|
||
arguments:
|
||
|
||
S - a short. (the handle ID)
|
||
Lorg/gnu/Emacs/EmacsWindow; - an instance of the EmacsWindow
|
||
class. (the parent)
|
||
IIII - four ints. (the window geometry.)
|
||
Z - a boolean. (whether or not the
|
||
window is override-redirect; see
|
||
XChangeWindowAttributes.)
|
||
|
||
old = class;
|
||
class = (*android_java_env)->NewGlobalRef (android_java_env, class);
|
||
(*android_java_env)->ExceptionClear (android_java_env);
|
||
ANDROID_DELETE_LOCAL_REF (old);
|
||
|
||
Next, it saves a global reference to the class and deletes the local
|
||
reference. Global references will never be deallocated by the Java
|
||
virtual machine as long as they still exist.
|
||
|
||
if (!class)
|
||
memory_full (0);
|
||
}
|
||
|
||
/* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window
|
||
creation time. */
|
||
override_redirect = ((value_mask
|
||
& ANDROID_CW_OVERRIDE_REDIRECT)
|
||
&& attrs->override_redirect);
|
||
|
||
object = (*android_java_env)->NewObject (android_java_env, class,
|
||
constructor, (jshort) window,
|
||
parent_object, (jint) x, (jint) y,
|
||
(jint) width, (jint) height,
|
||
(jboolean) override_redirect);
|
||
|
||
Then, it creates an instance of the ``EmacsWindow'' class with the
|
||
appropriate arguments and previously determined constructor.
|
||
|
||
if (!object)
|
||
{
|
||
(*android_java_env)->ExceptionClear (android_java_env);
|
||
|
||
max_handle = prev_max_handle;
|
||
memory_full (0);
|
||
|
||
If creating the object fails, Emacs clears the ``pending exception''
|
||
and signals that it is out of memory.
|
||
}
|
||
|
||
android_handles[window].type = ANDROID_HANDLE_WINDOW;
|
||
android_handles[window].handle
|
||
= (*android_java_env)->NewGlobalRef (android_java_env,
|
||
object);
|
||
(*android_java_env)->ExceptionClear (android_java_env);
|
||
ANDROID_DELETE_LOCAL_REF (object);
|
||
|
||
Otherwise, it associates a new global reference to the object with the
|
||
handle, and deletes the local reference returned from the JNI
|
||
NewObject function.
|
||
|
||
if (!android_handles[window].handle)
|
||
memory_full (0);
|
||
|
||
If allocating the global reference fails, Emacs signals that it is out
|
||
of memory.
|
||
|
||
android_change_window_attributes (window, value_mask, attrs);
|
||
return window;
|
||
|
||
Otherwise, it applies the specified window attributes and returns the
|
||
handle of the new window.
|
||
}
|
||
|
||
|
||
|
||
DRAWABLES, CURSORS AND HANDLES
|
||
|
||
Each widget created by Emacs corresponds to a single ``window'', which
|
||
has its own backing store. This arrangement is quite similar to X.
|
||
|
||
C code does not directly refer to the EmacsView widgets that implement
|
||
the UI logic behind windows. Instead, its handles refer to
|
||
EmacsWindow structures, which contain the state necessary to interact
|
||
with the widgets in an orderly and synchronized manner.
|
||
|
||
Like X, both pixmaps and windows are drawable resources, and the same
|
||
graphics operations can be applied to both. Thus, a separate
|
||
EmacsPixmap structure is used to wrap around Android Bitmap resources,
|
||
and the Java-level graphics operation functions are capable of
|
||
operating on them both.
|
||
|
||
Finally, graphics contexts are maintained on both the C and Java
|
||
levels; the C state recorded in `struct android_gc' is kept in sync
|
||
with the Java state in the GContext handle's corresponding EmacsGC
|
||
structure, and cursors are used through handles that refer to
|
||
EmacsCursor structures that hold system PointerIcons.
|
||
|
||
In all cases, the interfaces provided are identical to X.
|
||
|
||
|
||
|
||
EVENT LOOP
|
||
|
||
In a typical Android application, the event loop is managed by the
|
||
operating system, and callbacks (implemented through overriding
|
||
separate functions in widgets) are run by the event loop wherever
|
||
necessary. The thread which runs the event loop is also the only
|
||
thread capable of creating and manipulating widgets and activities,
|
||
and is referred to as the ``UI thread''.
|
||
|
||
These callbacks are used by Emacs to write representations of X-like
|
||
events to a separate event queue, which are then read from Emacs's own
|
||
event loop running in a separate thread. This is accomplished through
|
||
replacing `select' by a function which waits for the event queue to be
|
||
occupied, in addition to any file descriptors that `select' would
|
||
normally wait for.
|
||
|
||
Conversely, Emacs's event loop sometimes needs to send events to the
|
||
UI thread. These events are implemented as tiny fragments of code,
|
||
which are run as they are received by the main thread.
|
||
|
||
A typical example is `displayToast', which is implemented in
|
||
EmacsService.java:
|
||
|
||
public void
|
||
displayToast (final String string)
|
||
{
|
||
runOnUiThread (new Runnable () {
|
||
@Override
|
||
public void
|
||
run ()
|
||
{
|
||
Toast toast;
|
||
|
||
toast = Toast.makeText (getApplicationContext (),
|
||
string, Toast.LENGTH_SHORT);
|
||
toast.show ();
|
||
}
|
||
});
|
||
}
|
||
|
||
Here, the variable `string' is used by a nested function. This nested
|
||
function contains a copy of that variable, and is run on the main
|
||
thread using the function `runOnUiThread', in order to display a short
|
||
status message on the display.
|
||
|
||
When Emacs needs to wait for the nested function to finish, it uses a
|
||
mechanism implemented in `syncRunnable'. This mechanism first calls a
|
||
deadlock avoidance mechanism, then runs a nested function on the UI
|
||
thread, which is expected to signal itself as a condition variable
|
||
upon completion. It is typically used to allocate resources that can
|
||
only be allocated from the UI thread, or to obtain non-thread-safe
|
||
information. The following function is an example; it returns a new
|
||
EmacsView widget corresponding to the provided window:
|
||
|
||
public EmacsView
|
||
getEmacsView (final EmacsWindow window, final int visibility,
|
||
final boolean isFocusedByDefault)
|
||
{
|
||
Runnable runnable;
|
||
final EmacsHolder<EmacsView> view;
|
||
|
||
view = new EmacsHolder<EmacsView> ();
|
||
|
||
runnable = new Runnable () {
|
||
public void
|
||
run ()
|
||
{
|
||
synchronized (this)
|
||
{
|
||
view.thing = new EmacsView (window);
|
||
view.thing.setVisibility (visibility);
|
||
|
||
/* The following function is only present on Android 26
|
||
or later. */
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||
view.thing.setFocusedByDefault (isFocusedByDefault);
|
||
|
||
notify ();
|
||
}
|
||
}
|
||
};
|
||
|
||
syncRunnable (runnable);
|
||
return view.thing;
|
||
}
|
||
|
||
As no value can be directly returned from the nested function, a
|
||
separate container object is used to hold the result after the
|
||
function finishes execution. Note the type name inside the angle
|
||
brackets: this type is substituted into the class definition as it is
|
||
used; a definition such as:
|
||
|
||
public class Foo<T>
|
||
{
|
||
T bar;
|
||
};
|
||
|
||
can not be used alone:
|
||
|
||
Foo holder; /* Error! */
|
||
|
||
but must have a type specified:
|
||
|
||
Foo<Object> holder;
|
||
|
||
in which case the effective definition is:
|
||
|
||
public class Foo
|
||
{
|
||
Object bar;
|
||
};
|
||
|
||
|
||
|
||
COMPATIBILITY
|
||
|
||
There are three variables set within every Android application that
|
||
extert influence over the set of Android systems it supports, and the
|
||
measures it must take to function faithfully on each of those systems:
|
||
the minimum API level, compile SDK version and target API level.
|
||
|
||
The minimum API level is the earliest version of Android that is
|
||
permitted to install and run the application. For Emacs, this is
|
||
established by detecting the __ANDROID_API__ preprocessor macro
|
||
defined within the Android C compiler.
|
||
|
||
Before Java code executes any Android API calls that are not present
|
||
within Android 2.2 (API level 8), the lowest API level supported by
|
||
Emacs as a whole, it must first check the value of the:
|
||
|
||
Build.VERSION.SDK_INT
|
||
|
||
variable, which is always set to the API level of the system Emacs is
|
||
presently installed within. For example, before calling
|
||
`dispatchKeyEventFromInputMethod', a function absent from Android 6.0
|
||
(API level 23) or earlier, check:
|
||
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||
view.imManager.dispatchKeyEventFromInputMethod (view, key);
|
||
else
|
||
{
|
||
|
||
where `N' is a constant defined to 24.
|
||
|
||
The compile SDK version is the version of the Android SDK headers Java
|
||
code is compiled against. Because Java does not provide conditional
|
||
compilation constructs, Emacs can't be compiled with any version of
|
||
these headers other than the version mentioned in `java/INSTALL', but
|
||
the headers used do not affect the set of supported systems provided
|
||
that the version checks illustrated above are performed where
|
||
necessary.
|
||
|
||
The target API level is a number within java/AndroidManifest.xml.in
|
||
the system refers to when deciding whether to enable
|
||
backwards-incompatible modifications to the behavior of various system
|
||
APIs. For any given Android version, backwards incompatible changes
|
||
in that version will be disabled for applications whose target API
|
||
levels don't exceed its own.
|
||
|
||
The target API should nevertheless be updated to match every major
|
||
Android update, as Google has stated their intentions to prohibit
|
||
users from installing applications targeting ``out-of-date'' versions
|
||
of Android, though this threat has hitherto been made good on.
|
||
|
||
|
||
|
||
This file is part of GNU Emacs.
|
||
|
||
GNU Emacs is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
GNU Emacs is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|