[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
feature/android 8e4c5db193d 02/10: Update Android port
From: |
Po Lu |
Subject: |
feature/android 8e4c5db193d 02/10: Update Android port |
Date: |
Sun, 26 Feb 2023 02:12:16 -0500 (EST) |
branch: feature/android
commit 8e4c5db193dc7baee5846520fe8b63d8bea99148
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
Update Android port
* doc/emacs/android.texi (Android Startup, Android File System)
(Android Environment, Android Windowing, Android
Troubleshooting): Improve documentation; fix typos.
* doc/lispref/commands.texi (Misc Events): Likewise.
* java/org/gnu/emacs/EmacsService.java (queryBattery): New
function.
* lisp/battery.el (battery-status-function): Set appropriately
for Android.
(battery-android): New function.
* src/android.c (struct android_emacs_service): New method
`query_battery'.
(android_check_content_access): Improve exception checking.
(android_init_emacs_service): Look up new method.
(android_destroy_handle, android_create_window)
(android_init_android_rect_class, android_init_emacs_gc_class)
(android_set_clip_rectangles)
(android_create_pixmap_from_bitmap_data, android_fill_polygon)
(android_get_image, android_put_image, android_bell)
(android_set_input_focus, android_raise_window)
(android_lower_window, android_query_tree, android_get_geometry)
(android_translate_coordinates, android_wc_lookup_string)
(android_damage_window, android_build_string)
(android_build_jstring, android_exception_check_1)
(android_exception_check_2): New functions.
(android_browse_url): Improve exception handling. Always use
android_exception_check and don't leak local refs.
(android_query_battery): New function.
* src/android.h (struct android_battery_state): New struct.
* src/androidfns.c (Fandroid_query_battery, syms_of_androidfns):
New function.
* src/androidfont.c (androidfont_from_lisp, DO_SYMBOL_FIELD)
(DO_CARDINAL_FIELD, androidfont_list, androidfont_match)
(androidfont_draw, androidfont_open_font)
(androidfont_close_font):
* src/androidselect.c (Fandroid_set_clipboard)
(Fandroid_get_clipboard):
* src/sfnt.c (sfnt_map_glyf_table):
* src/sfntfont.c (sfntfont_free_outline_cache)
(sfntfont_free_raster_cache, sfntfont_close): Allow font close
functions to be called twice.
---
doc/emacs/android.texi | 21 ++-
doc/lispref/commands.texi | 2 +-
java/org/gnu/emacs/EmacsService.java | 57 ++++++++-
lisp/battery.el | 76 ++++++++++-
src/android.c | 239 ++++++++++++++++++++++-------------
src/android.h | 32 +++++
src/androidfns.c | 43 ++++++-
src/androidfont.c | 64 +++-------
src/androidselect.c | 11 +-
src/sfnt.c | 8 +-
src/sfntfont.c | 27 +++-
11 files changed, 414 insertions(+), 166 deletions(-)
diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi
index f176d68ae67..428cf1049b0 100644
--- a/doc/emacs/android.texi
+++ b/doc/emacs/android.texi
@@ -93,6 +93,10 @@ Enable ``developer options'' on your device, by going to the
``About''
page in the system settings application and clicking on the ``build
version'' or ``kernel version'' items five to seven times.
+@item
+Open the ``developer options'' settings page, which should be under
+the ``system'' page in the settings application.
+
@item
Turn on the switch ``USB debugging''.
@@ -194,8 +198,10 @@ when the user grants the ``Files and Media'' permission to
Emacs via
system settings.
@end itemize
- The external storage directory is found at @file{/sdcard}; the other
-directories are not found at any fixed location.
+ The external storage directory is found at @file{/sdcard}. The
+other directories are not found at any fixed location, although the
+app data directory is typically symlinked to
+@file{/data/data/org.gnu.emacs}.
@cindex file system limitations, Android 11
On Android 11 and later, the Android system restricts applications
@@ -242,7 +248,7 @@ they are packaged as libraries in the library directory,
because
otherwise the system will not unpack them while Emacs is being
installed. This means, instead of specifying @code{ctags} or
@code{emacsclient} in a subprocess, Lisp code must specify
-@code{libctags.so} or @code{libemacsclient.so} on the commnd line
+@code{libctags.so} or @code{libemacsclient.so} on the command line
instead when starting either of those programs in a subprocess.
The @file{/assets} directory containing Emacs start-up files is
@@ -274,7 +280,7 @@ unless the device is under memory stress.
such special treatment. However, Emacs applies a workaround: the
system considers applications that create a permanent notification to
be performing active work, and will avoid killing such applications.
-Thus, on those systems, Emacs displays a permanant notification for as
+Thus, on those systems, Emacs displays a permanent notification for as
long as it is running. Once the notification is displayed, it can be
safely hidden through the system settings without resulting in Emacs
being killed.
@@ -393,8 +399,9 @@ tiled on the screen at any time.
are created. Instead, the system may choose to terminate windows that
are not on screen in order to save memory, with the assumption that
the program will save its contents to disk and restore them later,
-when the user asks to open it again. As this is obviously not
-possible with Emacs, Emacs separates a frame from a system window.
+when the user asks for it to be opened again. As this is obviously
+not possible with Emacs, Emacs separates the resources associated with
+a frame from its system window.
Each system window created (including the initial window created
during Emacs startup) is appended to a list of windows that do not
@@ -537,7 +544,7 @@ Emacs.
The next time that same copy of Emacs starts up, it simply loads the
data contained in that dump file, greatly improving start up time.
- If by some unforseen circumstance the dump file is corrupted, Emacs
+ If by some unforeseen circumstance the dump file is corrupted, Emacs
can crash. If that happens, the dump file stored in the Emacs files
directory can be erased through the same preferences screen.
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 495e48bad3f..650178dc407 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -2221,7 +2221,7 @@ been made to them, use the variable
form:
@indentedblock
-@w{@code{(@var{buffer} @var{beg} @var{end} @var{ephemeral})}}
+@w{@code{((@var{buffer} @var{beg} @var{end} @var{ephemeral}) ...)}}
@end indentedblock
Where @var{ephemeral} is the buffer which was modified,
diff --git a/java/org/gnu/emacs/EmacsService.java
b/java/org/gnu/emacs/EmacsService.java
index c9701ff2990..48c7c743014 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -54,9 +54,9 @@ import android.content.res.AssetManager;
import android.database.Cursor;
import android.database.MatrixCursor;
-
import android.net.Uri;
+import android.os.BatteryManager;
import android.os.Build;
import android.os.Looper;
import android.os.IBinder;
@@ -762,4 +762,59 @@ public class EmacsService extends Service
return false;
}
}
+
+ /* Return the status of the battery. See struct
+ android_battery_status for the order of the elements
+ returned.
+
+ Value may be null upon failure. */
+
+ public long[]
+ queryBattery ()
+ {
+ Object tem;
+ BatteryManager manager;
+ long capacity, chargeCounter, currentAvg, currentNow;
+ long status, remaining;
+ int prop;
+
+ /* Android 4.4 or earlier require applications to listen to
+ changes to the battery instead of querying for its status. */
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+ return null;
+
+ tem = getSystemService (Context.BATTERY_SERVICE);
+ manager = (BatteryManager) tem;
+ remaining = -1;
+
+ prop = BatteryManager.BATTERY_PROPERTY_CAPACITY;
+ capacity = manager.getLongProperty (prop);
+ prop = BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER;
+ chargeCounter = manager.getLongProperty (prop);
+ prop = BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE;
+ currentAvg = manager.getLongProperty (prop);
+ prop = BatteryManager.BATTERY_PROPERTY_CURRENT_NOW;
+ currentNow = manager.getLongProperty (prop);
+
+ /* Return the battery status. N.B. that Android 7.1 and earlier
+ only return ``charging'' or ``discharging''. */
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ status = manager.getIntProperty (BatteryManager.BATTERY_PROPERTY_STATUS);
+ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+ status = (manager.isCharging ()
+ ? BatteryManager.BATTERY_STATUS_CHARGING
+ : BatteryManager.BATTERY_STATUS_DISCHARGING);
+ else
+ status = (currentNow > 0
+ ? BatteryManager.BATTERY_STATUS_CHARGING
+ : BatteryManager.BATTERY_STATUS_DISCHARGING);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
+ remaining = manager.computeChargeTimeRemaining ();
+
+ return new long[] { capacity, chargeCounter, currentAvg,
+ currentNow, remaining, status, };
+ }
};
diff --git a/lisp/battery.el b/lisp/battery.el
index 4306d5b2058..a2bbd463c12 100644
--- a/lisp/battery.el
+++ b/lisp/battery.el
@@ -29,9 +29,11 @@
;; - The `/sys/class/power_supply/' files of Linux >= 2.6.39.
;; - The `/proc/acpi/' directory structure of Linux 2.4.20 and 2.6.
;; - The `/proc/apm' file format of Linux version 1.3.58 or newer.
+;; - The Haiku ACPI battery driver.
;; - BSD by using the `apm' program.
;; - Darwin (macOS) by using the `pmset' program.
;; - Windows via the GetSystemPowerStatus API call.
+;; - Android 5 or later via the BatteryManager APIs.
;;; Code:
@@ -95,17 +97,22 @@ Value does not include \".\" or \"..\"."
(defcustom battery-status-function
(cond ((member battery-upower-service (dbus-list-activatable-names))
#'battery-upower)
- ((and (eq system-type 'gnu/linux)
+ ;; Try to find the relevant devices in /sys and /proc on
+ ;; Android as well, in case the system makes them available.
+ ((and (memq system-type '(gnu/linux android))
(file-readable-p "/sys/")
(battery--find-linux-sysfs-batteries))
#'battery-linux-sysfs)
- ((and (eq system-type 'gnu/linux)
+ ((and (memq system-type '(gnu/linux android))
(file-directory-p "/proc/acpi/battery"))
#'battery-linux-proc-acpi)
- ((and (eq system-type 'gnu/linux)
+ ((and (memq system-type '(gnu/linux android))
(file-readable-p "/proc/")
(file-readable-p "/proc/apm"))
#'battery-linux-proc-apm)
+ ;; Now try the Android battery status function.
+ ((eq system-type 'android)
+ #'battery-android)
((and (eq system-type 'berkeley-unix)
(file-executable-p "/usr/sbin/apm"))
#'battery-bsd-apm)
@@ -1072,6 +1079,69 @@ The following %-sequences are provided:
(cons ?t (or remaining-time "N/A")))))
+;;; `BatteryManager' interface for Android.
+
+(declare-function android-query-battery "androidfns.c")
+
+(defun battery-android ()
+ "Get battery status information using Android.
+
+The following %-sequences are provided:
+%c Current capacity (mAh)
+%r Current rate of charge or discharge (mA)
+%B Battery status (verbose)
+%b Battery status, empty means high, `-' means low,
+ `+' means charging and `?' means unknown.
+%p Battery load percentage.
+%m Remaining time (to charge) in minutes.
+%h Remaining time (to charge) in hours.
+%t Remaining time (to charge) in the form `h:min'."
+ (when-let* ((status (android-query-battery)))
+ (let* ((percentage nil)
+ (capacity nil)
+ (sym-status nil)
+ (symbol nil)
+ (rate nil)
+ (remaining nil)
+ (hours nil)
+ (minutes nil))
+ ;; Figure out the percentage.
+ (setq percentage (number-to-string (car status)))
+ ;; Figure out the capacity
+ (setq capacity (number-to-string (/ (cadr status) 1000)))
+ ;; Figure out the battery status.
+ (let ((percentage (car status)))
+ (cl-ecase (nth 4 status)
+ (2 (setq sym-status "charging" symbol "+"))
+ (3 (setq sym-status "discharging"
+ symbol (if (< percentage 15) "-" " ")))
+ (5 (setq sym-status "full" symbol " "))
+ (4 (setq sym-status "not charging"
+ symbol (if (< percentage 15) "-" " ")))
+ (1 (setq sym-status "unknown" symbol "?"))))
+ ;; Figure out the rate of charge.
+ (setq rate (/ (nth 3 status) 1000))
+ ;; Figure out the remaining time.
+ (let* ((time (nth 5 status))
+ (mins (/ time (* 1000 60)))
+ (hours-left (/ mins 60))
+ (mins (mod mins 60)))
+ (unless (eq time -1)
+ (setq remaining (format "%d:%d" hours-left mins)
+ hours (number-to-string hours-left)
+ minutes (number-to-string mins))))
+ ;; Return results.
+ (list (cons ?c capacity)
+ (cons ?p percentage)
+ (cons ?r rate)
+ (cons ?B sym-status)
+ (cons ?b symbol)
+ (cons ?m (or minutes "N/A"))
+ (cons ?h (or hours "N/A"))
+ (cons ?t (or remaining "N/A"))
+ (cons ?L "N/A")))))
+
+
;;; Private functions.
(defun battery-format (format alist)
diff --git a/src/android.c b/src/android.c
index 9c600be6cdf..72c50c0a13c 100644
--- a/src/android.c
+++ b/src/android.c
@@ -111,6 +111,7 @@ struct android_emacs_service
jmethodID reset_ic;
jmethodID open_content_uri;
jmethodID check_content_uri;
+ jmethodID query_battery;
};
struct android_emacs_pixmap
@@ -1050,7 +1051,7 @@ android_check_content_access (const char *filename, int
mode)
!= 0),
(jboolean) ((mode & W_OK)
!= 0));
- android_exception_check ();
+ android_exception_check_1 (string);
ANDROID_DELETE_LOCAL_REF (string);
return rc;
@@ -1998,6 +1999,7 @@ android_init_emacs_service (void)
"([BZZZ)I");
FIND_METHOD (check_content_uri, "checkContentUri",
"([BZZ)Z");
+ FIND_METHOD (query_battery, "queryBattery", "()[J");
#undef FIND_METHOD
}
@@ -2691,11 +2693,8 @@ android_destroy_handle (android_handle handle)
class
= (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) class);
- (*android_java_env)->ExceptionClear (android_java_env);
+ android_exception_check_1 (old);
ANDROID_DELETE_LOCAL_REF (old);
-
- if (!class)
- memory_full (0);
}
(*android_java_env)->CallVoidMethod (android_java_env,
@@ -2818,11 +2817,8 @@ android_create_window (android_window parent, int x, int
y,
old = class;
class = (*android_java_env)->NewGlobalRef (android_java_env, class);
- (*android_java_env)->ExceptionClear (android_java_env);
+ android_exception_check_1 (old);
ANDROID_DELETE_LOCAL_REF (old);
-
- if (!class)
- memory_full (0);
}
/* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window
@@ -2904,11 +2900,8 @@ android_init_android_rect_class (void)
android_rect_class
= (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) android_rect_class);
- (*android_java_env)->ExceptionClear (android_java_env);
+ android_exception_check_1 (old);
ANDROID_DELETE_LOCAL_REF (old);
-
- if (!android_rect_class)
- memory_full (0);
}
static void
@@ -2941,10 +2934,8 @@ android_init_emacs_gc_class (void)
emacs_gc_class
= (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) emacs_gc_class);
- (*android_java_env)->ExceptionClear (android_java_env);
+ android_exception_check_1 (old);
ANDROID_DELETE_LOCAL_REF (old);
- if (!emacs_gc_class)
- memory_full (0);
emacs_gc_foreground
= (*android_java_env)->GetFieldID (android_java_env,
@@ -3188,12 +3179,7 @@ android_set_clip_rectangles (struct android_gc *gc, int
clip_x_origin,
n_clip_rects,
android_rect_class,
NULL);
-
- if (!array)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
for (i = 0; i < n_clip_rects; ++i)
{
@@ -3207,12 +3193,10 @@ android_set_clip_rectangles (struct android_gc *gc, int
clip_x_origin,
(jint) (clip_rects[i].y
+ clip_rects[i].height));
- if (!rect)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- ANDROID_DELETE_LOCAL_REF (array);
- memory_full (0);
- }
+ /* The meaning of this call is to check whether or not an
+ allocation error happened, and to delete ARRAY and signal an
+ out-of-memory error if that is the case. */
+ android_exception_check_1 (array);
(*android_java_env)->SetObjectArrayElement (android_java_env,
array, i, rect);
@@ -3511,12 +3495,7 @@ android_create_pixmap_from_bitmap_data (char *data,
unsigned int width,
/* Create the color array holding the data. */
colors = (*android_java_env)->NewIntArray (android_java_env,
width * height);
-
- if (!colors)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
SAFE_NALLOCA (region, sizeof *region, width);
@@ -3666,12 +3645,7 @@ android_fill_polygon (android_drawable drawable, struct
android_gc *gc,
npoints,
point_class.class,
NULL);
-
- if (!array)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
for (i = 0; i < npoints; ++i)
{
@@ -3680,13 +3654,7 @@ android_fill_polygon (android_drawable drawable, struct
android_gc *gc,
point_class.constructor,
(jint) points[i].x,
(jint) points[i].y);
-
- if (!point)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- ANDROID_DELETE_LOCAL_REF (array);
- memory_full (0);
- }
+ android_exception_check_1 (array);
(*android_java_env)->SetObjectArrayElement (android_java_env,
array, i, point);
@@ -3978,12 +3946,9 @@ android_get_image (android_drawable handle,
bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
drawable,
drawable_class.get_bitmap);
- if (!bitmap)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
+ /* Clear the bitmap info structure. */
memset (&bitmap_info, 0, sizeof bitmap_info);
/* The NDK doc seems to imply this function can fail but doesn't say
@@ -4115,12 +4080,9 @@ android_put_image (android_pixmap handle, struct
android_image *image)
bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
drawable,
drawable_class.get_bitmap);
- if (!bitmap)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
+ /* Clear the bitmap info structure. */
memset (&bitmap_info, 0, sizeof bitmap_info);
/* The NDK doc seems to imply this function can fail but doesn't say
@@ -4196,6 +4158,7 @@ android_bell (void)
(*android_java_env)->CallVoidMethod (android_java_env,
emacs_service,
service_class.ring_bell);
+ android_exception_check ();
}
void
@@ -4210,6 +4173,7 @@ android_set_input_focus (android_window handle, unsigned
long time)
(*android_java_env)->CallVoidMethod (android_java_env, window,
make_input_focus, (jlong) time);
+ android_exception_check ();
}
void
@@ -4224,6 +4188,7 @@ android_raise_window (android_window handle)
(*android_java_env)->CallVoidMethod (android_java_env, window,
raise);
+ android_exception_check ();
}
void
@@ -4238,6 +4203,7 @@ android_lower_window (android_window handle)
(*android_java_env)->CallVoidMethod (android_java_env, window,
lower);
+ android_exception_check ();
}
int
@@ -4259,11 +4225,7 @@ android_query_tree (android_window handle,
android_window *root_return,
emacs_service,
service_class.query_tree,
window);
- if (!array)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
/* The first element of the array is the parent window. The rest
are the children. */
@@ -4315,11 +4277,7 @@ android_get_geometry (android_window handle,
= (*android_java_env)->CallObjectMethod (android_java_env,
window,
get_geometry);
- if (!window_geometry)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
/* window_geometry is an array containing x, y, width and
height. border_width is always 0 on Android. */
@@ -4380,12 +4338,7 @@ android_translate_coordinates (android_window src, int x,
= (*android_java_env)->CallObjectMethod (android_java_env,
window, method,
(jint) x, (jint) y);
-
- if (!coordinates)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
/* The array must contain two elements: X, Y translated to the root
window. */
@@ -4396,6 +4349,8 @@ android_translate_coordinates (android_window src, int x,
/* Obtain the coordinates from the array. */
ints = (*android_java_env)->GetIntArrayElements (android_java_env,
coordinates, NULL);
+ android_exception_check_1 (coordinates);
+
*root_x = ints[0];
*root_y = ints[1];
@@ -4492,7 +4447,7 @@ android_wc_lookup_string (android_key_pressed_event
*event,
/* Now return this input method string. */
characters = (*android_java_env)->GetStringChars (android_java_env,
string, NULL);
- android_exception_check ();
+ android_exception_check_1 (string);
/* Figure out how big the string is. */
size = (*android_java_env)->GetStringLength (android_java_env,
@@ -4517,7 +4472,6 @@ android_wc_lookup_string (android_key_pressed_event
*event,
(*android_java_env)->ReleaseStringChars (android_java_env, string,
characters);
- android_exception_check ();
ANDROID_DELETE_LOCAL_REF (string);
}
}
@@ -4604,17 +4558,14 @@ android_damage_window (android_drawable handle,
+ damage->width),
(jint) (damage->y
+ damage->height));
- if (!rect)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
/* Post the damage to the drawable. */
(*android_java_env)->CallVoidMethod (android_java_env,
drawable,
drawable_class.damage_rect,
rect);
+ android_exception_check_1 (rect);
ANDROID_DELETE_LOCAL_REF (rect);
}
@@ -5114,11 +5065,7 @@ android_build_string (Lisp_Object text)
not really of consequence. */
string = (*android_java_env)->NewStringUTF (android_java_env,
SSDATA (encoded));
- if (!string)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
return string;
}
@@ -5132,15 +5079,45 @@ android_build_jstring (const char *text)
string = (*android_java_env)->NewStringUTF (android_java_env,
text);
- if (!string)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
return string;
}
+
+
+/* Exception checking functions. Most JNI functions which allocate
+ memory return NULL upon failure; they also set the JNI
+ environment's pending exception to an OutOfMemoryError.
+
+ These functions check for such errors and call memory_full wherever
+ appropriate. Three variants are provided: one which releases no
+ local references, one which releases a single local reference
+ before calling memory_full, and one which releases two local
+ references.
+
+ Typically, you use these functions by calling them immediately
+ after a JNI function which allocates memory, passing it any local
+ references that are already valid but are not used after leaving
+ the current scope. For example, to allocate foo and then make
+ global_foo its global reference, and then release foo, you write:
+
+ jobject foo, global_foo;
+
+ foo = (*android_java_env)->New...;
+ android_exception_check ();
+
+ global_foo = (*android_java_env)->NewGlobalRef (..., foo);
+ android_exception_check_1 (foo);
+ ANDROID_DELETE_LOCAL_REF (foo);
+
+ where the first android_exception_check ensures that foo has been
+ allocated correctly, while the call to android_exception_check_1,
+ and the call to ANDROID_DELETE_LOCAL_REF afterwards, together
+ ensure the same of global_foo, and also that foo is released both
+ if global_foo cannot be allocated, and after the global reference
+ is created. */
+
/* Check for JNI exceptions and call memory_full in that
situation. */
@@ -5159,6 +5136,47 @@ android_exception_check (void)
}
}
+/* Check for JNI exceptions. If there is one such exception, clear
+ it, then delete the local reference to OBJECT and call
+ memory_full. */
+
+void
+android_exception_check_1 (jobject object)
+{
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "Possible out of memory error."
+ " The Java exception follows: ");
+ /* Describe exactly what went wrong. */
+ (*android_java_env)->ExceptionDescribe (android_java_env);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+ memory_full (0);
+ }
+}
+
+/* Like android_exception_check_one, except it takes more than one
+ local reference argument. */
+
+void
+android_exception_check_2 (jobject object, jobject object1)
+{
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "Possible out of memory error."
+ " The Java exception follows: ");
+ /* Describe exactly what went wrong. */
+ (*android_java_env)->ExceptionDescribe (android_java_env);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+ ANDROID_DELETE_LOCAL_REF (object1);
+ memory_full (0);
+ }
+}
+
+
/* Native image transforms. */
@@ -5446,7 +5464,7 @@ android_browse_url (Lisp_Object url)
buffer = (*android_java_env)->GetStringUTFChars (android_java_env,
(jstring) value,
NULL);
- android_exception_check ();
+ android_exception_check_1 (string);
/* Otherwise, build the string describing the error. */
tem = build_string_from_utf8 (buffer);
@@ -5490,6 +5508,45 @@ android_get_current_api_level (void)
return android_api_level;
}
+/* Query the status of the battery, and place it in *STATUS.
+ Value is 1 if the system is too old, else 0. */
+
+int
+android_query_battery (struct android_battery_state *status)
+{
+ jlongArray array;
+ jlong *longs;
+
+ array = (*android_java_env)->CallObjectMethod (android_java_env,
+ emacs_service,
+ service_class.query_battery);
+ android_exception_check ();
+
+ /* A NULL return with no exception means that battery information
+ could not be obtained. */
+
+ if (!array)
+ return 1;
+
+ longs = (*android_java_env)->GetLongArrayElements (android_java_env,
+ array, NULL);
+ android_exception_check_1 (array);
+
+ status->capacity = longs[0];
+ status->charge_counter = longs[1];
+ status->current_average = longs[2];
+ status->current_now = longs[3];
+ status->remaining = longs[4];
+ status->status = longs[5];
+
+ (*android_java_env)->ReleaseLongArrayElements (android_java_env,
+ array, longs,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (array);
+
+ return 0;
+}
+
/* Whether or not a query is currently being made. */
diff --git a/src/android.h b/src/android.h
index ec4fa33dfc3..01076c36b70 100644
--- a/src/android.h
+++ b/src/android.h
@@ -84,6 +84,8 @@ extern void android_set_dont_accept_focus (android_window,
bool);
extern jstring android_build_string (Lisp_Object);
extern jstring android_build_jstring (const char *);
extern void android_exception_check (void);
+extern void android_exception_check_1 (jobject);
+extern void android_exception_check_2 (jobject, jobject);
extern void android_get_keysym_name (int, char *, size_t);
extern void android_wait_event (void);
@@ -106,7 +108,37 @@ extern void android_closedir (struct android_dir *);
/* Very miscellaneous functions. */
+struct android_battery_state
+{
+ /* Battery charge level in integer percentage. */
+ intmax_t capacity;
+
+ /* Battery charge level in microampere-hours. */
+ intmax_t charge_counter;
+
+ /* Battery current in microampere-hours. */
+ intmax_t current_average;
+
+ /* Instantaneous battery current in microampere-hours. */
+ intmax_t current_now;
+
+ /* Estimate as to the amount of time remaining until the battery is
+ charged, in milliseconds. */
+ intmax_t remaining;
+
+ /* Battery status. The value is either:
+
+ 2, if the battery is charging.
+ 3, if the battery is discharging.
+ 5, if the battery is full.
+ 4, if the battery is not full or discharging,
+ but is not charging either.
+ 1, if the battery state is unknown. */
+ int status;
+};
+
extern Lisp_Object android_browse_url (Lisp_Object);
+extern int android_query_battery (struct android_battery_state *);
diff --git a/src/androidfns.c b/src/androidfns.c
index b5b88df4fe5..dc68cef8a02 100644
--- a/src/androidfns.c
+++ b/src/androidfns.c
@@ -2783,6 +2783,46 @@ frame_parm_handler android_frame_parm_handlers[] =
NULL,
};
+
+
+/* Battery information support. */
+
+DEFUN ("android-query-battery", Fandroid_query_battery,
+ Sandroid_query_battery, 0, 0, 0,
+ doc: /* Perform a query for battery information.
+This function will not work before Android 5.0.
+Value is nil upon failure, or a list of the form:
+
+ (CAPACITY CHARGE-COUNTER CURRENT-AVERAGE CURRENT-NOW STATUS
+ REMAINING)
+
+See the documentation at
+
+ https://developer.android.com/reference/android/os/BatteryManager
+
+for more details about these values. */)
+ (void)
+{
+ struct android_battery_state state;
+
+ /* Make sure the Android libraries have been initialized. */
+
+ if (!android_init_gui)
+ return Qnil;
+
+ /* Perform the query. */
+
+ if (android_query_battery (&state))
+ return Qnil;
+
+ return listn (6, make_int (state.capacity),
+ make_int (state.charge_counter),
+ make_int (state.current_average),
+ make_int (state.current_now),
+ make_int (state.status),
+ make_int (state.remaining));
+}
+
#endif
@@ -2837,8 +2877,9 @@ syms_of_androidfns (void)
defsubr (&Sx_hide_tip);
defsubr (&Sandroid_detect_mouse);
defsubr (&Sandroid_toggle_on_screen_keyboard);
-
#ifndef ANDROID_STUBIFY
+ defsubr (&Sandroid_query_battery);
+
tip_timer = Qnil;
staticpro (&tip_timer);
tip_frame = Qnil;
diff --git a/src/androidfont.c b/src/androidfont.c
index 9da82b670fd..1a09027bca7 100644
--- a/src/androidfont.c
+++ b/src/androidfont.c
@@ -431,12 +431,7 @@ androidfont_from_lisp (Lisp_Object font)
spec = (*android_java_env)->AllocObject (android_java_env,
font_spec_class.class);
-
- if (!spec)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
#define DO_SYMBOL_FIELD(field, index)
\
tem = AREF (font, index);
\
@@ -446,11 +441,7 @@ androidfont_from_lisp (Lisp_Object font)
not matter at all. */
\
string = (*android_java_env)->NewStringUTF (android_java_env,
\
SSDATA (SYMBOL_NAME (tem)));
\
- if (!string)
\
- {
\
- (*android_java_env)->ExceptionClear (android_java_env);
\
- memory_full (0);
\
- }
\
+ android_exception_check_1 (spec);
\
\
(*android_java_env)->SetObjectField (android_java_env, spec,
\
font_spec_class.field,
\
@@ -472,11 +463,7 @@ androidfont_from_lisp (Lisp_Object font)
integer_class.class,
\
integer_class.constructor,
\
(jint) value);
\
- if (!integer)
\
- {
\
- (*android_java_env)->ExceptionClear (android_java_env);
\
- memory_full (0);
\
- }
\
+ android_exception_check_1 (spec);
\
\
(*android_java_env)->SetObjectField (android_java_env, spec,
\
font_spec_class.field,
\
@@ -582,12 +569,9 @@ androidfont_list (struct frame *f, Lisp_Object font_spec)
font_driver,
font_driver_class.list,
spec);
- (*android_java_env)->ExceptionClear (android_java_env);
+ android_exception_check_1 (spec);
ANDROID_DELETE_LOCAL_REF (spec);
- if (!array)
- memory_full (0);
-
entities = (jarray) array;
size = (*android_java_env)->GetArrayLength (android_java_env,
entities);
@@ -613,12 +597,9 @@ androidfont_list (struct frame *f, Lisp_Object font_spec)
/* Now, make a global reference to the Java font entity. */
info->object = (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) tem);
- (*android_java_env)->ExceptionClear (android_java_env);
+ android_exception_check_2 (tem, entities);
ANDROID_DELETE_LOCAL_REF (tem);
- if (!info->object)
- memory_full (0);
-
value = Fcons (entity, value);
}
@@ -641,12 +622,9 @@ androidfont_match (struct frame *f, Lisp_Object font_spec)
font_driver,
font_driver_class.match,
spec);
- (*android_java_env)->ExceptionClear (android_java_env);
+ android_exception_check_1 (spec);
ANDROID_DELETE_LOCAL_REF (spec);
- if (!result)
- memory_full (0);
-
entity = font_make_entity_android (VECSIZE (struct androidfont_entity));
info = (struct androidfont_entity *) XFONT_ENTITY (entity);
@@ -658,12 +636,9 @@ androidfont_match (struct frame *f, Lisp_Object font_spec)
androidfont_from_java (result, entity);
info->object = (*android_java_env)->NewGlobalRef (android_java_env,
(jobject) result);
- (*android_java_env)->ExceptionClear (android_java_env);
+ android_exception_check_2 (entity, result);
ANDROID_DELETE_LOCAL_REF (result);
- if (!info->object)
- memory_full (0);
-
return entity;
}
@@ -688,12 +663,7 @@ androidfont_draw (struct glyph_string *s, int from, int to,
ANDROID_HANDLE_WINDOW);
chars = (*android_java_env)->NewIntArray (android_java_env,
to - from);
-
- if (!chars)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- memory_full (0);
- }
+ android_exception_check ();
(*android_java_env)->SetIntArrayRegion (android_java_env, chars,
0, to - from,
@@ -710,7 +680,7 @@ androidfont_draw (struct glyph_string *s, int from, int to,
chars, (jint) x, (jint) y,
(jint) s->width,
(jboolean) with_background);
- (*android_java_env)->ExceptionClear (android_java_env);
+ android_exception_check_1 (chars);
ANDROID_DELETE_LOCAL_REF (chars);
return rc;
@@ -769,16 +739,12 @@ androidfont_open_font (struct frame *f, Lisp_Object
font_entity,
font_driver_class.open_font,
entity->object,
(jint) pixel_size);
- if (!font_info->object)
- {
- (*android_java_env)->ExceptionClear (android_java_env);
- return Qnil;
- }
+ android_exception_check ();
old = font_info->object;
font_info->object
= (*android_java_env)->NewGlobalRef (android_java_env, old);
- (*android_java_env)->ExceptionClear (android_java_env);
+ android_exception_check_1 (old);
ANDROID_DELETE_LOCAL_REF (old);
if (!font_info->object)
@@ -839,14 +805,20 @@ androidfont_close_font (struct font *font)
xfree (info->metrics);
}
+ info->metrics = NULL;
+
/* If info->object is NULL, then FONT was unsuccessfully created,
- and there is no global reference that has to be deleted. */
+ and there is no global reference that has to be deleted.
+
+ Alternatively, FONT may have been closed by font_close_object,
+ with this function called from GC. */
if (!info->object)
return;
(*android_java_env)->DeleteGlobalRef (android_java_env,
info->object);
+ info->object = NULL;
}
static int
diff --git a/src/androidselect.c b/src/androidselect.c
index 4585d64b7e8..2d8f14bb90d 100644
--- a/src/androidselect.c
+++ b/src/androidselect.c
@@ -147,7 +147,7 @@ DEFUN ("android-set-clipboard", Fandroid_set_clipboard,
clipboard,
clipboard_class.set_clipboard,
bytes);
- android_exception_check ();
+ android_exception_check_1 (bytes);
ANDROID_DELETE_LOCAL_REF (bytes);
return Qnil;
@@ -172,18 +172,13 @@ Alternatively, return nil if the clipboard is empty. */)
= (*android_java_env)->CallObjectMethod (android_java_env,
clipboard,
method);
-
- if (!bytes)
- {
- android_exception_check ();
- return Qnil;
- }
+ android_exception_check ();
length = (*android_java_env)->GetArrayLength (android_java_env,
bytes);
data = (*android_java_env)->GetByteArrayElements (android_java_env,
bytes, NULL);
- android_exception_check ();
+ android_exception_check_1 (bytes);
string = make_unibyte_string ((char *) data, length);
diff --git a/src/sfnt.c b/src/sfnt.c
index f3656422eef..6d9d63db165 100644
--- a/src/sfnt.c
+++ b/src/sfnt.c
@@ -1634,12 +1634,7 @@ sfnt_map_glyf_table (int fd, struct sfnt_offset_subtable
*subtable)
PROT_READ, MAP_PRIVATE, fd, offset);
if (glyphs == MAP_FAILED)
- {
- fprintf (stderr, "sfnt_map_glyf_table: mmap: %s\n",
- strerror (errno));
-
- return NULL;
- }
+ return NULL;
/* An observation is that glyphs tend to be accessed in sequential
order and immediately after the font's glyph table is loaded. */
@@ -1656,6 +1651,7 @@ sfnt_map_glyf_table (int fd, struct sfnt_offset_subtable
*subtable)
glyf->size = directory->length;
glyf->glyphs = (unsigned char *) glyphs + map_offset;
glyf->start = glyphs;
+
return glyf;
}
diff --git a/src/sfntfont.c b/src/sfntfont.c
index 31557155e51..f38dc904dc2 100644
--- a/src/sfntfont.c
+++ b/src/sfntfont.c
@@ -1798,6 +1798,9 @@ sfntfont_free_outline_cache (struct sfnt_outline_cache
*cache)
sfntfont_dereference_outline (last->outline);
xfree (last);
}
+
+ cache->next = cache;
+ cache->last = cache;
}
/* Dereference the raster RASTER. Free it once refcount reaches
@@ -1913,6 +1916,9 @@ sfntfont_free_raster_cache (struct sfnt_raster_cache
*cache)
sfntfont_dereference_raster (last->raster);
xfree (last);
}
+
+ cache->next = cache;
+ cache->last = cache;
}
@@ -2664,8 +2670,8 @@ sfntfont_close (struct font *font)
xfree (info->hmtx);
#ifdef HAVE_MMAP
-
- if (info->glyf_table_mapped)
+ if (info->glyf_table_mapped
+ && info->glyf)
{
rc = sfnt_unmap_glyf_table (info->glyf);
@@ -2684,6 +2690,23 @@ sfntfont_close (struct font *font)
xfree (info->cvt);
xfree (info->interpreter);
+ /* Clear these fields. It seems that close can be called twice,
+ once during font driver destruction, and once during GC. */
+
+ info->cmap = NULL;
+ info->hhea = NULL;
+ info->maxp = NULL;
+ info->head = NULL;
+ info->hhea = NULL;
+ info->glyf = NULL;
+ info->loca_short = NULL;
+ info->loca_long = NULL;
+ info->cmap_data = NULL;
+ info->prep = NULL;
+ info->fpgm = NULL;
+ info->cvt = NULL;
+ info->interpreter = NULL;
+
#ifdef HAVE_MMAP
/* Unlink INFO. */
- feature/android updated (ea74f3c0678 -> 744f19c22f2), Po Lu, 2023/02/26
- feature/android 80f26cc3988 04/10: ; * src/android.c (android_open): Clean up unused variables., Po Lu, 2023/02/26
- feature/android 687f4fadde4 06/10: Merge remote-tracking branch 'origin/master' into feature/android, Po Lu, 2023/02/26
- feature/android 8e4c5db193d 02/10: Update Android port,
Po Lu <=
- feature/android df29bb71fc4 03/10: Update Android port, Po Lu, 2023/02/26
- feature/android 28f6add67f8 08/10: Merge remote-tracking branch 'origin/master' into feature/android, Po Lu, 2023/02/26
- feature/android 39ddf1855bb 07/10: Update Android port, Po Lu, 2023/02/26
- feature/android 8fa86cc7cd7 05/10: Update Android port, Po Lu, 2023/02/26
- feature/android 74a7d34361a 09/10: Update from gnulib, Po Lu, 2023/02/26
- feature/android d5cccfdc564 01/10: Merge remote-tracking branch 'origin/master' into feature/android, Po Lu, 2023/02/26
- feature/android 744f19c22f2 10/10: Get rid of android_lookup_method, Po Lu, 2023/02/26