root/java/org/gnu/emacs/EmacsNoninteractive.java

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

DEFINITIONS

This source file includes following definitions.
  1. main

     1 /* Communication module for Android terminals.  -*- c-file-style: "GNU" -*-
     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 package org.gnu.emacs;
    21 
    22 import android.os.Looper;
    23 import android.os.Build;
    24 
    25 import android.content.Context;
    26 import android.content.res.AssetManager;
    27 
    28 import java.lang.reflect.Constructor;
    29 import java.lang.reflect.Method;
    30 
    31 /* Noninteractive Emacs.
    32 
    33    This is the class that libandroid-emacs.so starts.
    34    libandroid-emacs.so figures out the system classpath, then starts
    35    dalvikvm with the framework jars.
    36 
    37    At that point, dalvikvm calls main, which sets up the main looper,
    38    creates an ActivityThread and attaches it to the main thread.
    39 
    40    Then, it obtains an application context for the LoadedApk in the
    41    application thread.
    42 
    43    Finally, it obtains the necessary context specific objects and
    44    initializes Emacs.  */
    45 
    46 @SuppressWarnings ("unchecked")
    47 public final class EmacsNoninteractive
    48 {
    49   public static void
    50   main (String[] args)
    51   {
    52     Object activityThread, loadedApk;
    53     Class activityThreadClass, loadedApkClass, contextImplClass;
    54     Class compatibilityInfoClass;
    55     Method method;
    56     Context context;
    57     AssetManager assets;
    58     String filesDir, libDir, cacheDir;
    59 
    60     Looper.prepare ();
    61     context = null;
    62     assets = null;
    63     filesDir = libDir = cacheDir = null;
    64 
    65     try
    66       {
    67         /* Get the activity thread.  */
    68         activityThreadClass = Class.forName ("android.app.ActivityThread");
    69 
    70         /* Get the systemMain method.  */
    71         method = activityThreadClass.getMethod ("systemMain");
    72 
    73         /* Create and attach the activity thread.  */
    74         activityThread = method.invoke (null);
    75         context = null;
    76 
    77         /* Now get an LoadedApk.  */
    78 
    79         try
    80           {
    81             loadedApkClass = Class.forName ("android.app.LoadedApk");
    82           }
    83         catch (ClassNotFoundException exception)
    84           {
    85             /* Android 2.2 has no LoadedApk class, but fortunately it
    86                does not need to be used, since contexts can be
    87                directly created.  */
    88 
    89             loadedApkClass = null;
    90             contextImplClass = Class.forName ("android.app.ContextImpl");
    91 
    92             method = activityThreadClass.getDeclaredMethod ("getSystemContext");
    93             context = (Context) method.invoke (activityThread);
    94             method = contextImplClass.getDeclaredMethod ("createPackageContext",
    95                                                          String.class,
    96                                                          int.class);
    97             method.setAccessible (true);
    98             context = (Context) method.invoke (context, "org.gnu.emacs",
    99                                                0);
   100           }
   101 
   102         /* If the context has not already been created, then do what
   103            is appropriate for newer versions of Android.  */
   104 
   105         if (context == null)
   106           {
   107             /* Get a LoadedApk.  How to do this varies by Android version.
   108                On Android 2.3.3 and earlier, there is no
   109                ``compatibilityInfo'' argument to getPackageInfo.  */
   110 
   111             if (Build.VERSION.SDK_INT
   112                 <= Build.VERSION_CODES.GINGERBREAD_MR1)
   113               {
   114                 method
   115                   = activityThreadClass.getMethod ("getPackageInfo",
   116                                                    String.class,
   117                                                    int.class);
   118                 loadedApk = method.invoke (activityThread, "org.gnu.emacs",
   119                                            0);
   120               }
   121             else
   122               {
   123                 compatibilityInfoClass
   124                   = Class.forName ("android.content.res.CompatibilityInfo");
   125 
   126                 method
   127                   = activityThreadClass.getMethod ("getPackageInfo",
   128                                                    String.class,
   129                                                    compatibilityInfoClass,
   130                                                    int.class);
   131                 loadedApk = method.invoke (activityThread, "org.gnu.emacs",
   132                                            null, 0);
   133               }
   134 
   135             if (loadedApk == null)
   136               throw new RuntimeException ("getPackageInfo returned NULL");
   137 
   138             /* Now, get a context.  */
   139             contextImplClass = Class.forName ("android.app.ContextImpl");
   140 
   141             try
   142               {
   143                 method
   144                   = contextImplClass.getDeclaredMethod ("createAppContext",
   145                                                         activityThreadClass,
   146                                                         loadedApkClass);
   147                 method.setAccessible (true);
   148                 context = (Context) method.invoke (null, activityThread,
   149                                                    loadedApk);
   150               }
   151             catch (NoSuchMethodException exception)
   152               {
   153                 /* Older Android versions don't have createAppContext, but
   154                    instead require creating a ContextImpl, and then
   155                    calling createPackageContext.  */
   156                 method
   157                   = activityThreadClass.getDeclaredMethod ("getSystemContext");
   158                 context = (Context) method.invoke (activityThread);
   159                 method
   160                   = contextImplClass.getDeclaredMethod ("createPackageContext",
   161                                                         String.class,
   162                                                         int.class);
   163                 method.setAccessible (true);
   164                 context = (Context) method.invoke (context, "org.gnu.emacs",
   165                                                    0);
   166               }
   167           }
   168 
   169         /* Don't actually start the looper or anything.  Instead, obtain
   170            an AssetManager.  */
   171         assets = context.getAssets ();
   172 
   173         /* Now configure Emacs.  The class path should already be set.  */
   174 
   175         filesDir = context.getFilesDir ().getCanonicalPath ();
   176         libDir = EmacsService.getLibraryDirectory (context);
   177         cacheDir = context.getCacheDir ().getCanonicalPath ();
   178       }
   179     catch (Exception e)
   180       {
   181         System.err.println ("Internal error: " + e);
   182         System.err.println ("This means that the Android platform changed,");
   183         System.err.println ("and that Emacs needs adjustments in order to");
   184         System.err.println ("obtain required system internal resources.");
   185         System.err.println ("Please report this bug to bug-gnu-emacs@gnu.org.");
   186         e.printStackTrace ();
   187 
   188         System.exit (1);
   189       }
   190 
   191     EmacsNative.setEmacsParams (assets, filesDir,
   192                                 libDir, cacheDir, 0.0f,
   193                                 0.0f, 0.0f, null, null,
   194                                 Build.VERSION.SDK_INT);
   195 
   196     /* Now find the dump file that Emacs should use, if it has already
   197        been dumped.  */
   198     EmacsApplication.findDumpFile (context);
   199 
   200     /* Start Emacs.  */
   201     EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
   202   }
   203 };

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