emacs-diffs
[Top][All Lists]
Advanced

[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.  */



reply via email to

[Prev in Thread] Current Thread [Next in Thread]