Awesome Ada

Hands-on: Ada programming 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.
18/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!.
03/10/2022 Due to termination of the xs4all provider web server, this page was moved to http://decijferij.nl/software/Ada on Android.html
28/11/2024
Improved Android Studio project.
Note that this solution also works for 32-bit Ada compilers for Android ARM.


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.
(Skip this step if you already have an Android Ada compiler installed, like GNAT Pro for Android)
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 (named 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... 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.app.Activity;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;

import android.os.Bundle;
import android.widget.EditText;

public class MainActivity extends Activity {
/* Called when the activity is first created. */

Process process = null;
BufferedReader reader;
BufferedWriter writer;
BufferedReader errReader;

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 log_reader = new BufferedReader(new InputStreamReader(pr.getInputStream()));
while ((read = log_reader.read(buffer1)) > 0) {
output.append(buffer1, 0, read);
t.append(output.toString() + "\n");
}
log_reader.close();

BufferedReader log_errReader = new BufferedReader(new InputStreamReader(pr.getErrorStream()));
while ((read = log_errReader.read(buffer1)) > 0) {
output.append(buffer1, 0, read);
t.append(output.toString() + "\n");
}
log_errReader.close();
}
catch (Exception e) {
t.append("logProcess: Exception " + e.getMessage());
e.printStackTrace();
}
}

public String get_result() throws IOException, InterruptedException {
String result = "";
int read;
char[] buffer1 = new char[4096];
StringBuffer output = new StringBuffer();
while (reader.ready() && (read = reader.read(buffer1)) > 0) {
output.append(buffer1, 0, read);
result += output.toString() + "\n";
}
return result;
}

@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");

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 */
process = Runtime.getRuntime().exec("." + data_dir + filename);

/* Open streams to this process. It should be running now */
/* and waiting for input. */
writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));

// Give the process time to settle
Thread.sleep(100);

log_text.append("\nProcess started\n\n");

/* 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: " + get_result() + "\n\n");

writer.write("Hello again from Java!" + '\n');
writer.flush();

/* Read the result */
log_text.append("Received: " + get_result() + "\n\n");

/* Make sure the Ada process terminates: */
writer.write("exit" + '\n');
writer.flush();

log_text.append("Received: " + get_result() + "\n\n");

} catch (IOException e) {
log_text.append("io exception: " + e.getMessage());
e.printStackTrace();
} catch (InterruptedException e) {
log_text.append("interrupted exception: " + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
log_text.append("exception: " + e.getMessage());
e.printStackTrace();
}
}
}

For Android wear devices I created a project that can be downloaded here. On your watch it will look something like this:

Android wear watch

You can download this project and import it into Android Studio.


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.
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 V8) 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.