Awesome Ada

Hands-on: Ada on Android and Android wear!
(using native code)
 
This hands-on describes how to use a native Ada application on an Android device (ARM processor) without having to 'root' your device. This page has been updated for using the Android Studio and Android wear devices.

11/02/2017
Minor update: The main process is now created using the ProcessBuilder.
25/10/2021
Update: The problem that caused this solution to stop working on devices running Android 10 or later has been solved!
This solution now works for old and more recent versions of Android!.


There has been some effort in getting native compilers to generate code for the Android platform. For C/C++ you could use the Android NDK which will help you to build and call a native code. However most other languages are not supported by the NDK (yet).
<Plug> If you can spend the money, there is now a very nice supported GNAT compiler available which allows integration of the Java and Ada development in Eclipse. </Plug>
But if you just want to try things out, this hands-on approach may still work for you :-)

There have been interesting posts on building a native gcc compiler for C/C++ and Fortran (Check out http://specificimpulses.blogspot.com/2011/01/my-android-speaks-fortran-yours-can-too.html)
But you can also use a native compiler for Ada! It can be found here (http://www.dragonlace.net/).
This native Ada compiler was ported to the FreeBSD OS by John Marino. Thanks John!

Now lets get started. Here are the major steps:
  1. Create a native Ada program for the ARM processor.
  2. Create an Android app in Eclipse or Android Studio.
  3. Deploy the app on your device.

1. Create a native Ada program for the ARM processor.


To build the Ada program, you need to install the GNAT-AUX Android (GNATDroid) compiler on a *BSD system.
Next I developed a small hello world Ada program:


with Ada.Text_IO; use Ada.Text_IO;

procedure hello_ada is
begin
   loop
      declare
         Line : String := Get_Line;
      begin
         Put_Line ("Hello from Ada!, You entered: " & Line);
         exit when Line'Length = 4 -- ' 
                   and then Line = "exit";
      end;
   end loop;
end hello_ada;
Now you can build the executable using the command:


arm-android-eabi-gnatmake hello_ada.adb -largs -fPIC -pie

Note that the flags '-largs -fPIC -pie' are optional for older phones but required for devices running Android lollipop and later versions (including wear devices).

Next, copy the executable (hello_ada) back to the Android development environment, in my case Windows.


2. Create the Android app.


For the development of Android apps, I use Windows but any OS that supports building Android apps should do as well.
Using the 'jniLibs/arm...' directory, native code can be added to the .apk file which can later be installed on your Android device (or even Android wear devices). This way the executable file gets the same owner id as the app, so that the app can run it without the need to have root privileges!
On the Android device, the executable 'libhello_ada.so' is stored in a /data/app/com/hello_ada-xxx/lib/arm(64) directory. This directory is protected, so your app can not modify any property (i.e. the name) of the copied executable.


Now it is time to add the Java code for interacting with Ada :-)

Here's the Java source (MainActivity.java) that does the trick:


package com.hello_ada; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import androidx.appcompat.app.AppCompatActivity; // import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.EditText; public class MainActivity extends AppCompatActivity { /* Called when the activity is first created. */ private void logProcess (Process pr, EditText t) throws IOException { /* This routine reads the output and error streams from a process */ /* and logs it to an EditText object. */ int read; char[] buffer1 = new char[4096]; StringBuffer output = new StringBuffer(); try { BufferedReader reader = new BufferedReader(new InputStreamReader(pr.getInputStream())); while ((read = reader.read(buffer1)) > 0) { output.append(buffer1, 0, read); t.append(output.toString() + "\n"); } reader.close(); BufferedReader errReader = new BufferedReader(new InputStreamReader(pr.getErrorStream())); while ((read = errReader.read(buffer1)) > 0) { output.append(buffer1, 0, read); t.append(output.toString() + "\n"); } errReader.close(); } catch (Exception e) { t.append("Exception " + e.getMessage()); e.printStackTrace(); } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); EditText log_text = (EditText) findViewById (R.id.editTextTextMultiLine); log_text.setText("Test native app\n\n"); Process process = null; Context context = getApplicationContext(); PackageManager pm = context.getPackageManager(); try { ApplicationInfo appInfo = pm.getApplicationInfo("com.hello_ada", PackageManager.GET_SHARED_LIBRARY_FILES); // log_text.append("native lib dir: " + appInfo.nativeLibraryDir + "\n\n"); String data_dir = appInfo.nativeLibraryDir; String filename = "/libhello_ada.so"; File hello_ada_file = new File (data_dir + filename); /* Check if the executable exists */ process = Runtime.getRuntime().exec("ls -l " + data_dir + filename); process.waitFor(); logProcess (process, log_text); /* Start the Ada process */ // You can use the ProcessBuilder or just call 'exec'. //ProcessBuilder prb = new ProcessBuilder ("." + data_dir + filename); //process = prb.start(); process = Runtime.getRuntime().exec("." + data_dir + filename); log_text.append("process started\n\n"); /* Open streams to this process. It should be running now */ /* and waiting for input. */ BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream())); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); /* Now you can send text (or commands!) to the Ada program */ writer.write("Hello from Java!\n"); writer.flush(); /* Read the result */ log_text.append("Received: " + reader.readLine() + "\n\n"); /* Make sure the Ada process terminates: */ writer.write("exit" + '\n'); writer.flush(); log_text.append("Received: " + reader.readLine() + "\n\n"); } catch (IOException e) { /* TODO Auto-generated catch block */ log_text.append("io exception: " + e.getMessage()); e.printStackTrace(); } catch (InterruptedException e) { /* TODO Auto-generated catch block */ log_text.append("interrupted exception: " + e.getMessage()); e.printStackTrace(); } catch (Exception e) { log_text.append("exception: " + e.getMessage()); e.printStackTrace(); } } }


3. Deploy the app on your device.

After you compiled and built this program, you should have an apk file in your project directory: '~/AndroidStudioProjects/hello_ada/app/build/outputs/apk' that you can copy to an Android device and install it. To do this, you must change the security settings on your device to allow installing apps from other sources.
As a side note, using bufferedReader to communicate with your fast Ada code obviously has a performance penalty so you should minimize your interactions (on a tablet a round trip communication takes less than half a millisecond).

For Android wear devices I created a project that can be downloaded here. (v7a)
You can download this project and import it into Android Studio. For debugging your smartwatch checkout this page.
On my fossil smartwatch it looks like this:

Android wear watch



Enjoy !
Rob


To make things easy, I already built the native module (ARM V7) you can integrate into your app. Or just download the complete Android Studio project.
If you just want to see it running, here's a hello_ada.apk app.
This version of the app should work on Android version 4.1 and higher.

If you have questions or comments, just send me an e-mail.