diff options
| author | Po Lu | 2023-02-25 20:11:48 +0800 |
|---|---|---|
| committer | Po Lu | 2023-02-25 20:11:48 +0800 |
| commit | df29bb71fc47b5e24fa971b668e4fa09dd6d244d (patch) | |
| tree | 78672c8e9f3b3db96f59a2bf6f81107d9eadb4af | |
| parent | 8e4c5db193dc7baee5846520fe8b63d8bea99148 (diff) | |
| download | emacs-df29bb71fc47b5e24fa971b668e4fa09dd6d244d.tar.gz emacs-df29bb71fc47b5e24fa971b668e4fa09dd6d244d.zip | |
Update Android port
* java/org/gnu/emacs/EmacsNoninteractive.java (main): Port to
Android 2.2.
* src/android-asset.h (AAsset_openFileDescriptor): Delete stub
function.
* src/android.c (android_check_compressed_file): Delete
function.
(android_open): Stop trying to find compressed files or to use
the system provided file descriptor. Explain why.
| -rw-r--r-- | java/org/gnu/emacs/EmacsNoninteractive.java | 118 | ||||
| -rw-r--r-- | src/android-asset.h | 9 | ||||
| -rw-r--r-- | src/android.c | 65 |
3 files changed, 87 insertions, 105 deletions
diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java index 4da82f2f894..30901edb75f 100644 --- a/java/org/gnu/emacs/EmacsNoninteractive.java +++ b/java/org/gnu/emacs/EmacsNoninteractive.java | |||
| @@ -87,56 +87,23 @@ public class EmacsNoninteractive | |||
| 87 | 87 | ||
| 88 | /* Create and attach the activity thread. */ | 88 | /* Create and attach the activity thread. */ |
| 89 | activityThread = method.invoke (null); | 89 | activityThread = method.invoke (null); |
| 90 | context = null; | ||
| 90 | 91 | ||
| 91 | /* Now get an LoadedApk. */ | 92 | /* Now get an LoadedApk. */ |
| 92 | loadedApkClass = Class.forName ("android.app.LoadedApk"); | ||
| 93 | 93 | ||
| 94 | /* Get a LoadedApk. How to do this varies by Android version. | 94 | try |
| 95 | On Android 2.3.3 and earlier, there is no | ||
| 96 | ``compatibilityInfo'' argument to getPackageInfo. */ | ||
| 97 | |||
| 98 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) | ||
| 99 | { | 95 | { |
| 100 | method | 96 | loadedApkClass = Class.forName ("android.app.LoadedApk"); |
| 101 | = activityThreadClass.getMethod ("getPackageInfo", | ||
| 102 | String.class, | ||
| 103 | int.class); | ||
| 104 | loadedApk = method.invoke (activityThread, "org.gnu.emacs", | ||
| 105 | 0); | ||
| 106 | } | 97 | } |
| 107 | else | 98 | catch (ClassNotFoundException exception) |
| 108 | { | 99 | { |
| 109 | compatibilityInfoClass | 100 | /* Android 2.2 has no LoadedApk class, but fortunately it |
| 110 | = Class.forName ("android.content.res.CompatibilityInfo"); | 101 | does not need to be used, since contexts can be |
| 111 | 102 | directly created. */ | |
| 112 | method | ||
| 113 | = activityThreadClass.getMethod ("getPackageInfo", | ||
| 114 | String.class, | ||
| 115 | compatibilityInfoClass, | ||
| 116 | int.class); | ||
| 117 | loadedApk = method.invoke (activityThread, "org.gnu.emacs", null, | ||
| 118 | 0); | ||
| 119 | } | ||
| 120 | |||
| 121 | if (loadedApk == null) | ||
| 122 | throw new RuntimeException ("getPackageInfo returned NULL"); | ||
| 123 | 103 | ||
| 124 | /* Now, get a context. */ | 104 | loadedApkClass = null; |
| 125 | contextImplClass = Class.forName ("android.app.ContextImpl"); | 105 | contextImplClass = Class.forName ("android.app.ContextImpl"); |
| 126 | 106 | ||
| 127 | try | ||
| 128 | { | ||
| 129 | method = contextImplClass.getDeclaredMethod ("createAppContext", | ||
| 130 | activityThreadClass, | ||
| 131 | loadedApkClass); | ||
| 132 | method.setAccessible (true); | ||
| 133 | context = (Context) method.invoke (null, activityThread, loadedApk); | ||
| 134 | } | ||
| 135 | catch (NoSuchMethodException exception) | ||
| 136 | { | ||
| 137 | /* Older Android versions don't have createAppContext, but | ||
| 138 | instead require creating a ContextImpl, and then | ||
| 139 | calling createPackageContext. */ | ||
| 140 | method = activityThreadClass.getDeclaredMethod ("getSystemContext"); | 107 | method = activityThreadClass.getDeclaredMethod ("getSystemContext"); |
| 141 | context = (Context) method.invoke (activityThread); | 108 | context = (Context) method.invoke (activityThread); |
| 142 | method = contextImplClass.getDeclaredMethod ("createPackageContext", | 109 | method = contextImplClass.getDeclaredMethod ("createPackageContext", |
| @@ -147,6 +114,73 @@ public class EmacsNoninteractive | |||
| 147 | 0); | 114 | 0); |
| 148 | } | 115 | } |
| 149 | 116 | ||
| 117 | /* If the context has not already been created, then do what | ||
| 118 | is appropriate for newer versions of Android. */ | ||
| 119 | |||
| 120 | if (context == null) | ||
| 121 | { | ||
| 122 | /* Get a LoadedApk. How to do this varies by Android version. | ||
| 123 | On Android 2.3.3 and earlier, there is no | ||
| 124 | ``compatibilityInfo'' argument to getPackageInfo. */ | ||
| 125 | |||
| 126 | if (Build.VERSION.SDK_INT | ||
| 127 | <= Build.VERSION_CODES.GINGERBREAD_MR1) | ||
| 128 | { | ||
| 129 | method | ||
| 130 | = activityThreadClass.getMethod ("getPackageInfo", | ||
| 131 | String.class, | ||
| 132 | int.class); | ||
| 133 | loadedApk = method.invoke (activityThread, "org.gnu.emacs", | ||
| 134 | 0); | ||
| 135 | } | ||
| 136 | else | ||
| 137 | { | ||
| 138 | compatibilityInfoClass | ||
| 139 | = Class.forName ("android.content.res.CompatibilityInfo"); | ||
| 140 | |||
| 141 | method | ||
| 142 | = activityThreadClass.getMethod ("getPackageInfo", | ||
| 143 | String.class, | ||
| 144 | compatibilityInfoClass, | ||
| 145 | int.class); | ||
| 146 | loadedApk = method.invoke (activityThread, "org.gnu.emacs", | ||
| 147 | null, 0); | ||
| 148 | } | ||
| 149 | |||
| 150 | if (loadedApk == null) | ||
| 151 | throw new RuntimeException ("getPackageInfo returned NULL"); | ||
| 152 | |||
| 153 | /* Now, get a context. */ | ||
| 154 | contextImplClass = Class.forName ("android.app.ContextImpl"); | ||
| 155 | |||
| 156 | try | ||
| 157 | { | ||
| 158 | method | ||
| 159 | = contextImplClass.getDeclaredMethod ("createAppContext", | ||
| 160 | activityThreadClass, | ||
| 161 | loadedApkClass); | ||
| 162 | method.setAccessible (true); | ||
| 163 | context = (Context) method.invoke (null, activityThread, | ||
| 164 | loadedApk); | ||
| 165 | } | ||
| 166 | catch (NoSuchMethodException exception) | ||
| 167 | { | ||
| 168 | /* Older Android versions don't have createAppContext, but | ||
| 169 | instead require creating a ContextImpl, and then | ||
| 170 | calling createPackageContext. */ | ||
| 171 | method | ||
| 172 | = activityThreadClass.getDeclaredMethod ("getSystemContext"); | ||
| 173 | context = (Context) method.invoke (activityThread); | ||
| 174 | method | ||
| 175 | = contextImplClass.getDeclaredMethod ("createPackageContext", | ||
| 176 | String.class, | ||
| 177 | int.class); | ||
| 178 | method.setAccessible (true); | ||
| 179 | context = (Context) method.invoke (context, "org.gnu.emacs", | ||
| 180 | 0); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 150 | /* Don't actually start the looper or anything. Instead, obtain | 184 | /* Don't actually start the looper or anything. Instead, obtain |
| 151 | an AssetManager. */ | 185 | an AssetManager. */ |
| 152 | assets = context.getAssets (); | 186 | assets = context.getAssets (); |
diff --git a/src/android-asset.h b/src/android-asset.h index b0e83bbf424..3b2f8105865 100644 --- a/src/android-asset.h +++ b/src/android-asset.h | |||
| @@ -365,15 +365,6 @@ android_asset_read_internal (AAsset *asset, int nbytes, char *buffer) | |||
| 365 | return total; | 365 | return total; |
| 366 | } | 366 | } |
| 367 | 367 | ||
| 368 | static int | ||
| 369 | AAsset_openFileDescriptor (AAsset *asset, off_t *out_start, | ||
| 370 | off_t *out_end) | ||
| 371 | { | ||
| 372 | *out_start = 0; | ||
| 373 | *out_end = 0; | ||
| 374 | return -1; | ||
| 375 | } | ||
| 376 | |||
| 377 | static long | 368 | static long |
| 378 | AAsset_getLength (AAsset *asset) | 369 | AAsset_getLength (AAsset *asset) |
| 379 | { | 370 | { |
diff --git a/src/android.c b/src/android.c index 72c50c0a13c..40d6234d508 100644 --- a/src/android.c +++ b/src/android.c | |||
| @@ -1376,42 +1376,6 @@ android_hack_asset_fd (AAsset *asset) | |||
| 1376 | #endif | 1376 | #endif |
| 1377 | } | 1377 | } |
| 1378 | 1378 | ||
| 1379 | /* Read two bytes from FD and see if they are ``PK'', denoting ZIP | ||
| 1380 | archive compressed data. If FD is -1, return -1. | ||
| 1381 | |||
| 1382 | If they are not, rewind the file descriptor to offset 0. | ||
| 1383 | |||
| 1384 | If either operation fails, return -1 and close FD. Else, value is | ||
| 1385 | FD. */ | ||
| 1386 | |||
| 1387 | static int | ||
| 1388 | android_check_compressed_file (int fd) | ||
| 1389 | { | ||
| 1390 | char bytes[2]; | ||
| 1391 | |||
| 1392 | if (fd == -1) | ||
| 1393 | return -1; | ||
| 1394 | |||
| 1395 | if (read (fd, bytes, 2) != 2) | ||
| 1396 | goto lseek_back; | ||
| 1397 | |||
| 1398 | if (bytes[0] != 'P' || bytes[1] != 'K') | ||
| 1399 | goto lseek_back; | ||
| 1400 | |||
| 1401 | /* This could be compressed data! */ | ||
| 1402 | return -1; | ||
| 1403 | |||
| 1404 | lseek_back: | ||
| 1405 | /* Seek back to offset 0. If this fails, return -1. */ | ||
| 1406 | if (lseek (fd, 0, SEEK_SET) != 0) | ||
| 1407 | { | ||
| 1408 | close (fd); | ||
| 1409 | return -1; | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | return fd; | ||
| 1413 | } | ||
| 1414 | |||
| 1415 | /* Make FD close-on-exec. If any system call fails, do not abort, but | 1379 | /* Make FD close-on-exec. If any system call fails, do not abort, but |
| 1416 | log a warning to the system log. */ | 1380 | log a warning to the system log. */ |
| 1417 | 1381 | ||
| @@ -1482,28 +1446,21 @@ android_open (const char *filename, int oflag, int mode) | |||
| 1482 | return -1; | 1446 | return -1; |
| 1483 | } | 1447 | } |
| 1484 | 1448 | ||
| 1485 | /* Try to obtain the file descriptor corresponding to this | 1449 | /* Create a shared memory file descriptor containing the asset |
| 1486 | asset. */ | 1450 | contents. |
| 1487 | fd = AAsset_openFileDescriptor (asset, &out_start, | 1451 | |
| 1488 | &out_length); | 1452 | The documentation misleads people into thinking that |
| 1453 | AAsset_openFileDescriptor does precisely this. However, it | ||
| 1454 | instead returns an offset into any uncompressed assets in the | ||
| 1455 | ZIP archive. This cannot be found in its documentation. */ | ||
| 1489 | 1456 | ||
| 1490 | /* The platform sometimes returns a file descriptor to ZIP | 1457 | fd = android_hack_asset_fd (asset); |
| 1491 | compressed data. Detect that and fall back to creating a | ||
| 1492 | shared memory file descriptor. */ | ||
| 1493 | fd = android_check_compressed_file (fd); | ||
| 1494 | 1458 | ||
| 1495 | if (fd == -1) | 1459 | if (fd == -1) |
| 1496 | { | 1460 | { |
| 1497 | /* The asset can't be accessed for some reason. Try to | 1461 | AAsset_close (asset); |
| 1498 | create a shared memory file descriptor. */ | 1462 | errno = ENXIO; |
| 1499 | fd = android_hack_asset_fd (asset); | 1463 | return -1; |
| 1500 | |||
| 1501 | if (fd == -1) | ||
| 1502 | { | ||
| 1503 | AAsset_close (asset); | ||
| 1504 | errno = ENXIO; | ||
| 1505 | return -1; | ||
| 1506 | } | ||
| 1507 | } | 1464 | } |
| 1508 | 1465 | ||
| 1509 | /* If O_CLOEXEC is specified, make the file descriptor close on | 1466 | /* If O_CLOEXEC is specified, make the file descriptor close on |