I cross-compiled my C application for Android ARM with arm-linux-gnueabi tool under Ubuntu 16.04 LTS. I compiled it with static linking flag. This C application is big and it has complicated makefile. It compiled successfully without any errors. But it behaves differently on Android phone and Ubuntu PC. More precisely i have two problems:
popen() and system() functions don't work on Android. They are carried out but do nothing and don't give any errors. This problem i solved with dirty hack.
fgets() functions works strange on Android.
1. About first problem.
I did small research and found that Android doesn't use ordinary libc library (glibc or another library which implements POSIX standard properly). It uses Bionic library instead of it (sorry, Android is new OS for me). I looked into popen() and system() functions code and noticed that these functions use _PATH_BSHELL macros. _PATH_BSHELL is path to the actual system shell. This path is "/system/bin/sh" on Android and "/bin/sh" on Ubuntu.
When i understood it i tried to hook popen() and system() functions. I copied code of these functions from the Bionic source, than i defined macros #define _MY_PATH_BSHELL "/system/bin/sh" and replaced calls like execve(_MY_PATH_BSHELL, argp, environ); by execve(_MY_PATH_BSHELL, argp, environ); calls. So it started work properly.
2. About second problem.
On Ubuntu this code works properly:
is_received = false;
while(!is_received) {
FILE *cmd = popen(command, "r");
is_received = fgets(buf, sizeof(buf), cmd) == NULL ? false : true;
}
But on Android fgets() always returns NULL and this loop works infinitely long. I tried to use read() function instead of fgets() and it worked.
On Android this code with read() works properly:
is_received = false;
while(!is_received) {
FILE *cmd = hooked_popen(command, "r");
int fd = fileno(cmd);
is_received = read(fd, buf, sizeof(buf)) == 0 ? false : true;
}
My questions.
How to solve my problems with popen() and system() neatly and correctly? I think i have to link statically with Bionic library. Is it right? How can i do it in console without Android Studio? I read that it is necessary to use NDK but it is not clear to me how.
Why fgets() behavior isn't similar on Android and Ubuntu?
Related
I am working on a library that is supported on many platforms including Android. This library is unit tested with the Google Test infrastructure. I am currently using Visual Studio 2015's cross platform features to build, deploy and debug. The test suite is getting considerably large and it takes a while to run the whole thing. With the Google Test infrastructure, it is possible to pass a command line argument to filter out the tests to be ran (--gtest_filter...). For platforms such as Windows, PS4 and XboxOne, this is easy to achieve inside Visual Studio by setting it in Project Properties -->Configuration Properties --> Debugging --> Command Arguments. However for the cross-platform developpment projects and package (AndroidProj), this Debugger property doesn't seem to exist. Here'a snapshot of Android Native-Activity Project Properties.
I am aware that the Visual GDB extension offers a similar feature to the one I am looking for as shown here, but this is not a viable option as I would like to avoid paying for the extension license.
How can I provide command line arguments to the Android Debugger the same way I can do it for the Local Windows Debugger for example?
A colleague of mine found the answer not too long after I posted this question. Here's how we solved our issue.
In VS2015, in the property pages of the Android cross-platform Package/App project (Androidproj), more precisely in the Debugging tab, there is the Launch Activity field. This field can be used to pass command line intent arguments. When you launch the debugger after installing the package, it will execute the following command:
adb.exe am start -D -n com.YourPackageName/TheContentOfLaunchActivityField.
In the Launch Activity field by default the launcher activity is specified (android.app.NativeActivity), but you can provide further intent arguments. A list of them can be found in this documentation: https://developer.android.com/studio/command-line/adb.html#IntentSpec.
In our situation, we needed to provide string data to our app (e.g.: --gtest_filter=...), so we used the intent argument -e extra_key extra_value.
Inside our main function we can retrieve the values of the extras using the following piece of code and some parsing (not shown). It uses the JNI (Java Native Interface).
JNIEnv* env = NULL;
state->activity->vm->AttachCurrentThread(&env, 0);
Private::JNIObjRef nativeActivityObject(env, state->activity->clazz);
jclass nativeActivityClass = env->GetObjectClass(nativeActivityObject.Get());
jmethodID getIntentID = env->GetMethodID(nativeActivityClass, "getIntent", "()Landroid/content/Intent;");
Private::JNIObjRef intentObject(env, env->CallObjectMethod(nativeActivityObject.Get(), getIntentID));
jclass intentClass = env->FindClass("android/content/Intent");
jmethodID getExtrasID = env->GetMethodID(intentClass, "getExtras", "()Landroid/os/Bundle;");
Private::JNIObjRef extrasObject(env, env->CallObjectMethod(intentObject.Get(), getExtrasID));
Is there a template to create a .so file that can be loaded by another delphi ape file - I have tried starting a blank fire monkey project and changing program to library and build it but the .so file that it produces won't load with dlopen within another delphi project. I have read that in other development environments there is a islibrary setting. I guess more to the point is there an example .so library built with fire monkey - I have found the bare bones link without fire monkey but it uses just jni not androidapi.jni - thanks
If you start a blank Firemonkey project and change Unit to Library, you'll get this compiler error:
[DCC Error] myfunnylib.pas(1): E2029 'UNIT' expected but 'LIBRARY' found
Trying to add an existing library project to the project group will separate said project from the rest of the build and assign unique build targets and platforms to it. Which will leave you with options to compile for Windows and OS X.
The only way I've heard of so far is to pre-compile your library with another compiler. FPC has been mentioned elsewhere. I haven't tried that yet but its next on the list.
http://wiki.freepascal.org/Android
Don't get confused by the fact that every Android app is in fact a shared object with the extension .so (for shared object). However, this is not the same thing as a shared library.
Because a library exports its functions while an application does not. To a compiler, that IS quite a difference though you wouldn't see that by looking at the file extension (but its prefix lib instead).
If you restrict your question to XE and Firemonkey, my only suggestion here would be to look into Android services. A bound local service might offer similar capabilities you'd expect from a library:
http://docwiki.embarcadero.com/RADStudio/Seattle/en/Android_Service
It's also important to note that Android N will not allow dynamic linking anymore so many of the methods you'll find elsewhere as a solution won't work.
suat dmk's code example in the answer below, though up-voted, is misleading.
It cannot be compiled for Android or iOS in XE 10.1 or any of its predecessors.
UPDATE:
There's a definite statement from Embarcadero staff regarding this issue.
It took a bit of patience in explaining the question, but the reply is clear enough:
[..] wants Delphi to have a project type of Shared Library(.so),
in this case then he is right, Delphi does not have it right now. [..]
quod erat demonstrandum
Hence there can be no such template. Also answers this question.
the following codes may be useful.
//-- Code for libMylib.so (MyLib.dproj) ---
library MyLib;
// uses SysUtils;
function GetSum(a, b: integer ) : integer; cdecl;
begin
Result := a + b;
end;
exports
GetSum name 'GetSum';
begin
end.
//-- Code for using libMylib.so (TestMyLib.dproj) ---
var
DocDir, LibFN: string;
GetSum: function(a, b: integer ) : integer; cdecl;
hLib: HMODULE;
begin
DocDir := IncludeTrailingPathDelimiter(System.IOUtils.TPath.GetLibrary`enter code here`Path);
LibFN:=DocDir + 'libMyLib.so';
hLib := LoadLibrary(LibFN); //ERROR (Segmentation fault (11))
if hLib<>0 then
begin
GetSum := GetProcAddress(hLib, 'GetSum');//It works
ShowMessage(IntToStr(GetSum(3, 8)));//It works
end;
end;
P.S: you must add compiled libMyLib.so file to Deployment of TestMyLib.dproj.
P.S2: it gives an error while executing "LoadLibrary", but it Works.
I couldn't find a solution. Probably it is related with compiler/linker options of MyLib.dproj. Because when I test another '.so' file which is compiled with C++, no problem occurs while calling LoadLibrary.
I am attempting to build an Android app that makes use of boost serialization. I have built the library against NDK r8d using arm 4.7's g++. When I go to compile my native code into a library using ndk-build, however, I get "undefined reference to 'mbtowc'" and "undefined reference to 'wctomb'" when the compiler attempts to link some code from Archive headers in boost.
I cannot seem to get a clear answer as to whether the NDK supports these functions.
Although it implements the functions, the CrystaX NDK is not an option as it has known crashes when using it with Boost, according to the Boost mailing list.
So, if the NDK does implement these functions somehow, why is NDK-build unable to link against them? I can find reference to them in cstdlib within the NDK, and I believe there may be a flag I need to set, but I'm not sure how or where to do so.
If there is no implementation of them, does anyone have any advice on how I can write them myself? I know roughly what mbtowc and its complement are supposed to do, but without much experience writing low-level C, and without much knowledge of Android / ARM architecture, I could really use some advice on doing so.
#ifdef ANDROID
int wctomb(char *s, wchar_t wc) { return wcrtomb(s,wc,NULL); }
int mbtowc(wchar_t *pwc, const char *s, size_t n) { return mbrtowc(pwc, s, n, NULL); }
#endif
Findings! Yes, boost has options to build it without requiring wchar support by adding preprocessor definitions to the boost build scripts. Boost still crashes when built using them at the line of code where I attempt to serialize an object to file. (the crash is a generic segfault at 0x00000000, and I was unable to gain any useful information).
So, I haven't bothered writing wctomb or its inverse. I shouldn't need to, is the answer to my question, although in the end the answer to the question doesn't matter.
To further clarify: don't bother doing what I've been trying to do. If you have been using Boost's serialization library and want your code to run on android ndk r8d or earlier, just give up. Write a serializer yourself, because there is no useful information to be found on how to make Serialization work. Hopefully a future release of the ndk fixes this problem, but for now I have no choice but to just rewrite everything.
I had the same problem. I was creating an android port for code that uses boost::serialization.
My code failed to compile because of missing implementation of mbtowc / wctomb (mb: multi-byte (char), wc: (wide-char)'
Implementation of these functions will probably be tricky cause as far as I know the wide-char for android is a single byte char instead of a true wide character..
However when I compiled boost with -DBOOST_NO_STD_WSTRING the link errors disappeared and I could use boost::archive::text_iarchive & boost::archive::text_oarchive on android.
I am now serializing STL containers of numerics and std::strings without a problem.
I compiled boost for android following the advice on this link: http://www.codexperiments.com/android/2011/05/tips-tricks-building-boost-with-ndk-r5/
I am using boost-1.55_0 and NDK r9c.
I added the following <compileflags>-DBOOST_NO_STD_WSTRING to the build command so it would build serialization library without wide character support.
Getting "system.entrypointnotfoundexception: loadlibrary" While trying to use SevenZipLib.dll to uncompress the .7z file containing media contents/file in the Android evn.
Context:
-The whole program is written in c# as a MONO Android Project. No Build/Deployment Error/warnings.
While running the apk, its throwing "system.entrypointnotfoundexception: loadlibrary".
-Also tested the same code as windows project (not mono) - uncompressing in the windows evn.
Assumptions for the issue:
7zip internally might be using COM components & Mono frame work is not supporting.
Question:
Has anyone come across similar issue? Please suggest some alternative dll/framework which can be used by my apk for uncompressing the .7z file.
Assuming that SevenZipLib.dll is the SevenZipLib Library on CodePlex, the problem is SevenZipLib\SevelZipLib\SevenZipArchive.cs:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern SafeLibraryHandle LoadLibrary(
[MarshalAs(UnmanagedType.LPTStr)] string lpFileName);
The project contains numerous P/Invokes into kernel32.dll (LoadLibrary(), GetProcAddress(), FreeLibrary()), ole32.dll (PropVariantClear()), oleaut32.dll (SafeArrayCreateVector()), and more.
In short, this library is intimately tied to Windows, and isn't going to work on a non-Windows platform in any meaningful fashion, not without a lot of work.
If you need 7z support in a Mono for Android application, you'll need to look into a different library. It looks like the 7-zip SDK includes C# source for reading LZMA files that doesn't rely on P/Invoke, so perhaps that would work?
I want to compile this program for Android and see it run on my phone:
#include "Hello World.h"
using namespace codewerks;
//=============================================
// Main Loop
//=============================================
int main(int argc, char* argv[])
{
Print(std::string("Hello World!"));
}
Where do I start? Can I compile this with GCC? The NDK seems focused on Java. Thank you.
This is now possible with the latest NDK. You will need an emulator or phone running Android 2.3 to try it, but the NativeActivity documentation has a complete example.
Unfortunately it is somewhat more complicated than a simple "hello world" example, and "main" is spelled "android_main". You still need to worry about your application life cycle as you do in Java, and the only real way to draw to the screen is to use OpenGL ES. It seems to be designed for writing games for Android.
Build as executable. (BUILD_EXECUTABLE)
Copy the executable to sdcard. (adb push)
Go to android shell. (adb shell)
Change the permission of the executable. (chmod 777)
Run the executable. (./out)
You will see the printed result on the console. (happy?)