root/src/android-asset.h

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. AAssetManager_fromJava
  2. AAssetManager_open
  3. AAsset_close
  4. android_asset_create_stream
  5. android_asset_read_internal
  6. AAsset_getLength
  7. AAsset_getBuffer
  8. AAsset_read
  9. AAsset_seek

     1 /* Android initialization for GNU Emacs.
     2 
     3 Copyright (C) 2023 Free Software Foundation, Inc.
     4 
     5 This file is part of GNU Emacs.
     6 
     7 GNU Emacs is free software: you can redistribute it and/or modify
     8 it under the terms of the GNU General Public License as published by
     9 the Free Software Foundation, either version 3 of the License, or (at
    10 your option) any later version.
    11 
    12 GNU Emacs is distributed in the hope that it will be useful,
    13 but WITHOUT ANY WARRANTY; without even the implied warranty of
    14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15 GNU General Public License for more details.
    16 
    17 You should have received a copy of the GNU General Public License
    18 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    19 
    20 #include <android/log.h>
    21 
    22 /* This file contains an emulation of the Android asset manager API
    23    used on builds for Android 2.2.  It is included by android.c
    24    whenever appropriate.
    25 
    26    The replacements in this file are not thread safe and must only be
    27    called from the creating thread.  */
    28 
    29 struct android_asset_manager
    30 {
    31   /* JNI environment.  */
    32   JNIEnv *env;
    33 
    34   /* Asset manager class and functions.  */
    35   jclass class;
    36   jmethodID open_fd;
    37 
    38   /* Asset file descriptor class and functions.  */
    39   jclass fd_class;
    40   jmethodID get_length;
    41   jmethodID create_input_stream;
    42   jmethodID close;
    43 
    44   /* Input stream class and functions.  */
    45   jclass input_stream_class;
    46   jmethodID read;
    47   jmethodID stream_close;
    48 
    49   /* Associated asset manager object.  */
    50   jobject asset_manager;
    51 };
    52 
    53 typedef struct android_asset_manager AAssetManager;
    54 
    55 struct android_asset
    56 {
    57   /* The asset manager.  */
    58   AAssetManager *manager;
    59 
    60   /* The length of the asset, or -1.  */
    61   jlong length;
    62 
    63   /* The asset file descriptor and input stream.  */
    64   jobject fd, stream;
    65 
    66   /* The mode.  */
    67   int mode;
    68 };
    69 
    70 typedef struct android_asset AAsset;
    71 
    72 static AAssetManager *
    73 AAssetManager_fromJava (JNIEnv *env, jobject java_manager)
    74 {
    75   AAssetManager *manager;
    76   jclass temp;
    77 
    78   manager = malloc (sizeof *manager);
    79 
    80   if (!manager)
    81     return NULL;
    82 
    83   manager->env = env;
    84   manager->asset_manager
    85     = (*env)->NewGlobalRef (env, java_manager);
    86 
    87   if (!manager->asset_manager)
    88     {
    89       free (manager);
    90       return NULL;
    91     }
    92 
    93   manager->class
    94     = (*env)->FindClass (env, "android/content/res/AssetManager");
    95   assert (manager->class);
    96 
    97   manager->open_fd
    98     = (*env)->GetMethodID (env, manager->class, "openFd",
    99                            "(Ljava/lang/String;)"
   100                            "Landroid/content/res/AssetFileDescriptor;");
   101   assert (manager->open);
   102 
   103   manager->fd_class
   104     = (*env)->FindClass (env, "android/content/res/AssetFileDescriptor");
   105   assert (manager->fd_class);
   106 
   107   manager->get_length
   108     = (*env)->GetMethodID (env, manager->fd_class, "getLength",
   109                            "()J");
   110   assert (manager->get_length);
   111 
   112   manager->create_input_stream
   113     = (*env)->GetMethodID (env, manager->fd_class,
   114                            "createInputStream",
   115                            "()Ljava/io/FileInputStream;");
   116   assert (manager->create_input_stream);
   117 
   118   manager->close
   119     = (*env)->GetMethodID (env, manager->fd_class,
   120                            "close", "()V");
   121   assert (manager->close);
   122 
   123   manager->input_stream_class
   124     = (*env)->FindClass (env, "java/io/InputStream");
   125   assert (manager->input_stream_class);
   126 
   127   manager->read
   128     = (*env)->GetMethodID (env, manager->input_stream_class,
   129                            "read", "([B)I");
   130   assert (manager->read);
   131 
   132   manager->stream_close
   133     = (*env)->GetMethodID (env, manager->input_stream_class,
   134                            "close", "()V");
   135   assert (manager->stream_close);
   136 
   137   /* Now convert all the class references to global ones.  */
   138   temp = manager->class;
   139   manager->class
   140     = (*env)->NewGlobalRef (env, temp);
   141   assert (manager->class);
   142   (*env)->DeleteLocalRef (env, temp);
   143   temp = manager->fd_class;
   144   manager->fd_class
   145     = (*env)->NewGlobalRef (env, temp);
   146   assert (manager->fd_class);
   147   (*env)->DeleteLocalRef (env, temp);
   148   temp = manager->input_stream_class;
   149   manager->input_stream_class
   150     = (*env)->NewGlobalRef (env, temp);
   151   assert (manager->input_stream_class);
   152   (*env)->DeleteLocalRef (env, temp);
   153 
   154   /* Return the asset manager.  */
   155   return manager;
   156 }
   157 
   158 enum
   159   {
   160     AASSET_MODE_STREAMING = 0,
   161     AASSET_MODE_BUFFER    = 1,
   162   };
   163 
   164 static AAsset *
   165 AAssetManager_open (AAssetManager *manager, const char *c_name,
   166                     int mode)
   167 {
   168   jobject desc;
   169   jstring name;
   170   AAsset *asset;
   171 
   172   /* Push a local frame.  */
   173   asset = NULL;
   174 
   175   (*(manager->env))->PushLocalFrame (manager->env, 3);
   176 
   177   if ((*(manager->env))->ExceptionCheck (manager->env))
   178     goto fail;
   179 
   180   /* Encoding issues can be ignored for now as there are only ASCII
   181      file names in Emacs.  */
   182   name = (*(manager->env))->NewStringUTF (manager->env, c_name);
   183 
   184   if (!name)
   185     goto fail;
   186 
   187   /* Now try to open an ``AssetFileDescriptor''.  */
   188   desc = (*(manager->env))->CallObjectMethod (manager->env,
   189                                               manager->asset_manager,
   190                                               manager->open_fd,
   191                                               name);
   192 
   193   if (!desc)
   194     goto fail;
   195 
   196   /* Allocate the asset.  */
   197   asset = calloc (1, sizeof *asset);
   198 
   199   if (!asset)
   200     {
   201       (*(manager->env))->CallVoidMethod (manager->env,
   202                                          desc,
   203                                          manager->close);
   204       goto fail;
   205     }
   206 
   207   /* Pop the local frame and return desc.  */
   208   desc = (*(manager->env))->NewGlobalRef (manager->env, desc);
   209 
   210   if (!desc)
   211     goto fail;
   212 
   213   (*(manager->env))->PopLocalFrame (manager->env, NULL);
   214 
   215   asset->manager = manager;
   216   asset->length = -1;
   217   asset->fd = desc;
   218   asset->mode = mode;
   219 
   220   return asset;
   221 
   222  fail:
   223   (*(manager->env))->ExceptionClear (manager->env);
   224   (*(manager->env))->PopLocalFrame (manager->env, NULL);
   225   free (asset);
   226 
   227   return NULL;
   228 }
   229 
   230 static AAsset *
   231 AAsset_close (AAsset *asset)
   232 {
   233   JNIEnv *env;
   234 
   235   env = asset->manager->env;
   236 
   237   (*env)->CallVoidMethod (asset->manager->env,
   238                           asset->fd,
   239                           asset->manager->close);
   240   (*env)->DeleteGlobalRef (asset->manager->env,
   241                            asset->fd);
   242 
   243   if (asset->stream)
   244     {
   245       (*env)->CallVoidMethod (asset->manager->env,
   246                               asset->stream,
   247                               asset->manager->stream_close);
   248       (*env)->DeleteGlobalRef (asset->manager->env,
   249                                asset->stream);
   250     }
   251 
   252   free (asset);
   253 }
   254 
   255 /* Create an input stream associated with the given ASSET.  Set
   256    ASSET->stream to its global reference.
   257 
   258    Value is 1 upon failure, else 0.  ASSET must not already have an
   259    input stream.  */
   260 
   261 static int
   262 android_asset_create_stream (AAsset *asset)
   263 {
   264   jobject stream;
   265   JNIEnv *env;
   266 
   267   env = asset->manager->env;
   268   stream
   269     = (*env)->CallObjectMethod (env, asset->fd,
   270                                 asset->manager->create_input_stream);
   271 
   272   if (!stream)
   273     {
   274       (*env)->ExceptionClear (env);
   275       return 1;
   276     }
   277 
   278   asset->stream
   279     = (*env)->NewGlobalRef (env, stream);
   280 
   281   if (!asset->stream)
   282     {
   283       (*env)->ExceptionClear (env);
   284       (*env)->DeleteLocalRef (env, stream);
   285       return 1;
   286     }
   287 
   288   (*env)->DeleteLocalRef (env, stream);
   289   return 0;
   290 }
   291 
   292 /* Read NBYTES from the specified asset into the given BUFFER;
   293 
   294    Internally, allocate a Java byte array containing 4096 elements and
   295    copy the data to and from that array.
   296 
   297    Value is the number of bytes actually read, 0 at EOF, or -1 upon
   298    failure, in which case errno is set accordingly.  If NBYTES is
   299    zero, behavior is undefined.  */
   300 
   301 static int
   302 android_asset_read_internal (AAsset *asset, int nbytes, char *buffer)
   303 {
   304   jbyteArray stash;
   305   JNIEnv *env;
   306   jint bytes_read, total;
   307 
   308   /* Allocate a suitable amount of storage.  Either nbytes or 4096,
   309      whichever is larger.  */
   310   env = asset->manager->env;
   311   stash = (*env)->NewByteArray (env, MIN (nbytes, 4096));
   312 
   313   if (!stash)
   314     {
   315       (*env)->ExceptionClear (env);
   316       errno = ENOMEM;
   317       return -1;
   318     }
   319 
   320   /* Try to create an input stream.  */
   321 
   322   if (!asset->stream
   323       && android_asset_create_stream (asset))
   324     {
   325       (*env)->DeleteLocalRef (env, stash);
   326       errno = ENOMEM;
   327       return -1;
   328     }
   329 
   330   /* Start reading.  */
   331 
   332   total = 0;
   333 
   334   while (nbytes)
   335     {
   336       bytes_read = (*env)->CallIntMethod (env, asset->stream,
   337                                           asset->manager->read,
   338                                           stash);
   339 
   340       /* Detect error conditions.  */
   341 
   342       if ((*env)->ExceptionCheck (env))
   343         goto out;
   344 
   345       /* Detect EOF.  */
   346 
   347       if (bytes_read == -1)
   348         goto out;
   349 
   350       /* Finally write out the amount that was read.  */
   351       bytes_read = MIN (bytes_read, nbytes);
   352       (*env)->GetByteArrayRegion (env, stash, 0, bytes_read, buffer);
   353 
   354       buffer += bytes_read;
   355       total += bytes_read;
   356       nbytes -= bytes_read;
   357     }
   358 
   359   /* Make sure the value of nbytes still makes sense.  */
   360   assert (nbytes >= 0);
   361 
   362  out:
   363   (*env)->ExceptionClear (env);
   364   (*env)->DeleteLocalRef (env, stash);
   365   return total;
   366 }
   367 
   368 static long
   369 AAsset_getLength (AAsset *asset)
   370 {
   371   JNIEnv *env;
   372 
   373   if (asset->length != -1)
   374     return asset->length;
   375 
   376   env = asset->manager->env;
   377   asset->length
   378     = (*env)->CallLongMethod (env, asset->fd,
   379                               asset->manager->get_length);
   380   return asset->length;
   381 }
   382 
   383 static char *
   384 AAsset_getBuffer (AAsset *asset)
   385 {
   386   long length;
   387   char *buffer;
   388 
   389   length = AAsset_getLength (asset);
   390 
   391   if (!length)
   392     return NULL;
   393 
   394   buffer = malloc (length);
   395 
   396   if (!buffer)
   397     return NULL;
   398 
   399   if (android_asset_read_internal (asset, length, buffer)
   400       != length)
   401     {
   402       xfree (buffer);
   403       return NULL;
   404     }
   405 
   406   return buffer;
   407 }
   408 
   409 static size_t
   410 AAsset_read (AAsset *asset, void *buffer, size_t size)
   411 {
   412   return android_asset_read_internal (asset, MIN (size, INT_MAX),
   413                                       buffer);
   414 }
   415 
   416 static off_t
   417 AAsset_seek (AAsset *asset, off_t offset, int whence)
   418 {
   419   /* Java InputStreams don't support seeking at all.  */
   420   errno = ESPIPE;
   421   return -1;
   422 }

/* [<][>][^][v][top][bottom][index][help] */