]> git.eshelyaron.com Git - emacs.git/commitdiff
Update Android port
authorPo Lu <luangruo@yahoo.com>
Sat, 25 Feb 2023 11:11:07 +0000 (19:11 +0800)
committerPo Lu <luangruo@yahoo.com>
Sat, 25 Feb 2023 11:11:07 +0000 (19:11 +0800)
* 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
doc/lispref/commands.texi
java/org/gnu/emacs/EmacsService.java
lisp/battery.el
src/android.c
src/android.h
src/androidfns.c
src/androidfont.c
src/androidselect.c
src/sfnt.c
src/sfntfont.c

index f176d68ae67b9ab0e90625773cf89bcb0cca12fd..428cf1049b0816278770aedae61e0b7e8ba7239d 100644 (file)
@@ -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.
 
index 495e48bad3f012f310eae4f690000c5de7b6a455..650178dc407f20b779a300ab23ef9e8ff8c6b71a 100644 (file)
@@ -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,
index c9701ff2990c68549d8bf12c84eecb60799499c7..48c7c743014433bbe262bd509bad4314b7187c2f 100644 (file)
@@ -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, };
+  }
 };
index 4306d5b20583e390d8e2200d39a7d6a4d58e5536..a2bbd463c12b1dd3daae5b5ae1299b21db679ec5 100644 (file)
 ;; - 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)
@@ -1071,6 +1078,69 @@ The following %-sequences are provided:
          (cons ?m (or minutes "N/A"))
          (cons ?t (or remaining-time "N/A")))))
 
+\f
+;;; `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")))))
+
 \f
 ;;; Private functions.
 
index 9c600be6cdf0d530a7bc6772c9967afd35dab14b..72c50c0a13c9c5f48a729f8a8f05c24419892742 100644 (file)
@@ -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;
 }
 
+\f
+
+/* 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);
+    }
+}
+
+
 \f
 
 /* 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;
+}
+
 \f
 
 /* Whether or not a query is currently being made.  */
index ec4fa33dfc39eaecb215a8e201b0365cd6e17a64..01076c36b70fb14a048a03803447f09cbb653c99 100644 (file)
@@ -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 *);
 
 \f
 
index b5b88df4fe572668f4faea5112c24399bdcbd47d..dc68cef8a021af85faf8233b7fc34c6a6085caac 100644 (file)
@@ -2783,6 +2783,46 @@ frame_parm_handler android_frame_parm_handlers[] =
   NULL,
 };
 
+\f
+
+/* 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
 
 \f
@@ -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;
index 9da82b670fdbca22483953fba42b51734e6b171e..1a09027bca790142c0e91886f7295d4fc197700f 100644 (file)
@@ -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
index 4585d64b7e854aea493383255cf543335abb9a69..2d8f14bb90d20db0a28e3f39a35691d194db83fe 100644 (file)
@@ -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);
 
index f3656422eef1838093cfae3209474160994d952a..6d9d63db165bb44aacd9a9481faf5e6b8ecdd662 100644 (file)
@@ -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;
 }
 
index 31557155e51a525c3646eea3934e5a2536a5ed57..f38dc904dc21c164d359cd9048b1d59d9664eb64 100644 (file)
@@ -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;
 }
 
 \f
@@ -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.  */