Implement native binder code of c++ - android

I have the following code for native binder implementation.
#define LOG_TAG "binder_demo"
#include <stdlib.h>
#include <utils/RefBase.h>
#include <utils/Log.h>
#include <binder/TextOutput.h>
#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
using namespace android;
#define INFO(...) \
do { \
printf(__VA_ARGS__); \
printf("\n"); \
ALOGD(__VA_ARGS__); \
} while(0)
void assert_fail(const char *file, int line, const char *func, const char *expr) {
INFO("assertion failed at file %s, line %d, function %s:",
file, line, func);
INFO("%s", expr);
abort();
}
#define ASSERT(e) \
do { \
if (!(e)) \
assert_fail(__FILE__, __LINE__, __func__, #e); \
} while(0)
#define PLOG aout
class IDemo : public IInterface {
public:
enum {
ALERT = IBinder::FIRST_CALL_TRANSACTION,
PUSH,
ADD
};
// Sends a user-provided value to the service
virtual void push(int32_t data) = 0;
// Sends a fixed alert string to the service
virtual void alert() = 0;
// Requests the service to perform an addition and return the result
virtual int32_t add(int32_t v1, int32_t v2) = 0;
DECLARE_META_INTERFACE(Demo); // Expands to 5 lines below:
};
// Client
class BpDemo : public BpInterface<IDemo> {
public:
BpDemo(const sp<IBinder>& impl) : BpInterface<IDemo>(impl) {
ALOGD("BpDemo::BpDemo()");
}
virtual void push(int32_t push_data) {
Parcel data, reply;
data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
data.writeInt32(push_data);
aout << "BpDemo::push parcel to be sent:\n";
data.print(PLOG); endl(PLOG);
remote()->transact(PUSH, data, &reply);
aout << "BpDemo::push parcel reply:\n";
reply.print(PLOG); endl(PLOG);
ALOGD("BpDemo::push(%i)", push_data);
}
virtual void alert() {
Parcel data, reply;
data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
data.writeString16(String16("The alert string"));
remote()->transact(ALERT, data, &reply, IBinder::FLAG_ONEWAY); // asynchronous call
ALOGD("BpDemo::alert()");
}
virtual int32_t add(int32_t v1, int32_t v2) {
Parcel data, reply;
data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
data.writeInt32(v1);
data.writeInt32(v2);
aout << "BpDemo::add parcel to be sent:\n";
data.print(PLOG); endl(PLOG);
remote()->transact(ADD, data, &reply);
ALOGD("BpDemo::add transact reply");
reply.print(PLOG); endl(PLOG);
int32_t res;
status_t status = reply.readInt32(&res);
ALOGD("BpDemo::add(%i, %i) = %i (status: %i)", v1, v2, res, status);
return res;
}
};
DECLARE_META_INTERFACE(Demo,"Demo"); // Expands to 5 lines below:
// Server
class BnDemo : public BnInterface<IDemo> {
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};
status_t BnDemo::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
ALOGD("BnDemo::onTransact(%i) %i", code, flags);
data.checkInterface(this);
data.print(PLOG); endl(PLOG);
switch(code) {
case ALERT: {
alert(); // Ignoring the fixed alert string
return NO_ERROR;
} break;
case PUSH: {
int32_t inData = data.readInt32();
ALOGD("BnDemo::onTransact got %i", inData);
push(inData);
ASSERT(reply != 0);
reply->print(PLOG); endl(PLOG);
return NO_ERROR;
} break;
case ADD: {
int32_t inV1 = data.readInt32();
int32_t inV2 = data.readInt32();
int32_t sum = add(inV1, inV2);
ALOGD("BnDemo::onTransact add(%i, %i) = %i", inV1, inV2, sum);
ASSERT(reply != 0);
reply->print(PLOG); endl(PLOG);
reply->writeInt32(sum);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
class Demo : public BnDemo {
virtual void push(int32_t data) {
INFO("Demo::push(%i)", data);
}
virtual void alert() {
INFO("Demo::alert()");
}
virtual int32_t add(int32_t v1, int32_t v2) {
INFO("Demo::add(%i, %i)", v1, v2);
return v1 + v2;
}
};
// Helper function to get a hold of the "Demo" service.
sp<IDemo> getDemoServ() {
sp<IServiceManager> sm = defaultServiceManager();
ASSERT(sm != 0);
sp<IBinder> binder = sm->getService(String16("Demo"));
// TODO: If the "Demo" service is not running, getService times out and binder == 0.
ASSERT(binder != 0);
sp<IDemo> demo = interface_cast<IDemo>(binder);
ASSERT(demo != 0);
return demo;
}
int main(int argc, char **argv) {
if (argc == 1) {
ALOGD("We're the service");
defaultServiceManager()->addService(String16("Demo"), new Demo());
android::ProcessState::self()->startThreadPool();
ALOGD("Demo service is now ready");
IPCThreadState::self()->joinThreadPool();
ALOGD("Demo service thread joined");
} else if (argc == 2) {
INFO("We're the client: %s", argv[1]);
int v = atoi(argv[1]);
sp<IDemo> demo = getDemoServ()
demo->alert();
demo->push(v);
const int32_t adder = 5;
int32_t sum = demo->add(v, adder);
ALOGD("Addition result: %i + %i = %i", v, adder, sum);
}
return 0;
}
The problem is how to compile and test it on a android platform. I don't want to build a new binary. What I want is that the code should compile on a running android platform and result should be displayed on a terminal like adb shell.

Related

Understanding and implementing native Binders in android using C++

I want to implement a simple IPC mechanism using Binders in android. For that, I searched on the Internet and found this. I compiled it and it runs fine on my Android device. I tried to take an overall understanding of the program, by searching for each class on AOSP, but everything got more difficult and messed up. Can anyone please explain (just high level), maybe by adding more comments, so that it also helps some future visitors. Here's the code is taken from there:
#define LOG_TAG "binder_demo"
#include <stdlib.h>
#include "utils/RefBase.h"
#include "utils/Log.h"
#include "utils/TextOutput.h"
#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
using namespace android;
#define INFO(...) \
do { \
printf(__VA_ARGS__); \
printf("\n"); \
LOGD(__VA_ARGS__); \
} while(0)
void assert_fail(const char *file, int line, const char *func, const char *expr) {
INFO("assertion failed at file %s, line %d, function %s:",
file, line, func);
INFO("%s", expr);
abort();
}
#define ASSERT(e) \
do { \
if (!(e)) \
assert_fail(__FILE__, __LINE__, __func__, #e); \
} while(0)
// Where to print the parcel contents: aout, alog, aerr. alog doesn't seem to work.
#define PLOG aout
// Interface (our AIDL) - Shared by server and client
class IDemo : public IInterface {
public:
enum {
ALERT = IBinder::FIRST_CALL_TRANSACTION,
PUSH,
ADD
};
// Sends a user-provided value to the service
virtual void push(int32_t data) = 0;
// Sends a fixed alert string to the service
virtual void alert() = 0;
// Requests the service to perform an addition and return the result
virtual int32_t add(int32_t v1, int32_t v2) = 0;
DECLARE_META_INTERFACE(Demo); // Expands to 5 lines below:
//static const android::String16 descriptor;
//static android::sp<IDemo> asInterface(const android::sp<android::IBinder>& obj);
//virtual const android::String16& getInterfaceDescriptor() const;
//IDemo();
//virtual ~IDemo();
};
// Client
class BpDemo : public BpInterface<IDemo> {
public:
BpDemo(const sp<IBinder>& impl) : BpInterface<IDemo>(impl) {
LOGD("BpDemo::BpDemo()");
}
virtual void push(int32_t push_data) {
Parcel data, reply;
data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
data.writeInt32(push_data);
aout << "BpDemo::push parcel to be sent:\n";
data.print(PLOG); endl(PLOG);
remote()->transact(PUSH, data, &reply);
aout << "BpDemo::push parcel reply:\n";
reply.print(PLOG); endl(PLOG);
LOGD("BpDemo::push(%i)", push_data);
}
virtual void alert() {
Parcel data, reply;
data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
data.writeString16(String16("The alert string"));
remote()->transact(ALERT, data, &reply, IBinder::FLAG_ONEWAY); // asynchronous call
LOGD("BpDemo::alert()");
}
virtual int32_t add(int32_t v1, int32_t v2) {
Parcel data, reply;
data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
data.writeInt32(v1);
data.writeInt32(v2);
aout << "BpDemo::add parcel to be sent:\n";
data.print(PLOG); endl(PLOG);
remote()->transact(ADD, data, &reply);
LOGD("BpDemo::add transact reply");
reply.print(PLOG); endl(PLOG);
int32_t res;
status_t status = reply.readInt32(&res);
LOGD("BpDemo::add(%i, %i) = %i (status: %i)", v1, v2, res, status);
return res;
}
};
//IMPLEMENT_META_INTERFACE(Demo, "Demo");
// Macro above expands to code below. Doing it by hand so we can log ctor and destructor calls.
const android::String16 IDemo::descriptor("Demo");
const android::String16& IDemo::getInterfaceDescriptor() const {
return IDemo::descriptor;
}
android::sp<IDemo> IDemo::asInterface(const android::sp<android::IBinder>& obj) {
android::sp<IDemo> intr;
if (obj != NULL) {
intr = static_cast<IDemo*>(obj->queryLocalInterface(IDemo::descriptor).get());
if (intr == NULL) {
intr = new BpDemo(obj);
}
}
return intr;
}
IDemo::IDemo() { LOGD("IDemo::IDemo()"); }
IDemo::~IDemo() { LOGD("IDemo::~IDemo()"); }
// End of macro expansion
// Server
class BnDemo : public BnInterface<IDemo> {
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};
status_t BnDemo::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
LOGD("BnDemo::onTransact(%i) %i", code, flags);
data.checkInterface(this);
data.print(PLOG); endl(PLOG);
switch(code) {
case ALERT: {
alert(); // Ignoring the fixed alert string
return NO_ERROR;
} break;
case PUSH: {
int32_t inData = data.readInt32();
LOGD("BnDemo::onTransact got %i", inData);
push(inData);
ASSERT(reply != 0);
reply->print(PLOG); endl(PLOG);
return NO_ERROR;
} break;
case ADD: {
int32_t inV1 = data.readInt32();
int32_t inV2 = data.readInt32();
int32_t sum = add(inV1, inV2);
LOGD("BnDemo::onTransact add(%i, %i) = %i", inV1, inV2, sum);
ASSERT(reply != 0);
reply->print(PLOG); endl(PLOG);
reply->writeInt32(sum);
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
class Demo : public BnDemo {
virtual void push(int32_t data) {
INFO("Demo::push(%i)", data);
}
virtual void alert() {
INFO("Demo::alert()");
}
virtual int32_t add(int32_t v1, int32_t v2) {
INFO("Demo::add(%i, %i)", v1, v2);
return v1 + v2;
}
};
// Helper function to get a hold of the "Demo" service.
sp<IDemo> getDemoServ() {
sp<IServiceManager> sm = defaultServiceManager();
ASSERT(sm != 0);
sp<IBinder> binder = sm->getService(String16("Demo"));
// TODO: If the "Demo" service is not running, getService times out and binder == 0.
ASSERT(binder != 0);
sp<IDemo> demo = interface_cast<IDemo>(binder);
ASSERT(demo != 0);
return demo;
}
int main(int argc, char **argv) {
if (argc == 1) {
LOGD("We're the service");
defaultServiceManager()->addService(String16("Demo"), new Demo());
android::ProcessState::self()->startThreadPool();
LOGD("Demo service is now ready");
IPCThreadState::self()->joinThreadPool();
LOGD("Demo service thread joined");
} else if (argc == 2) {
INFO("We're the client: %s", argv[1]);
int v = atoi(argv[1]);
sp<IDemo> demo = getDemoServ();
demo->alert();
demo->push(v);
const int32_t adder = 5;
int32_t sum = demo->add(v, adder);
LOGD("Addition result: %i + %i = %i", v, adder, sum);
}
return 0;
}
I know this is a bit late but checkout this amazing explanation by Gabriel Burca on Android IPC mechanism here. You can find a working example with a very simple C++ code from the same author here. Further it has clear instructions how to compile and add it to your AOSP source. Cheers!

android linux kernel communicate with user space about NETLINK_USER

I want to implements kernel communicate with user space,Here is my code:
#include <sys/socket.h>
#include <linux/netlink.h>
#include <stddef.h>
#define NETLINK_USER 16
#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
void main()
{
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
printf("%d\n",sock_fd);
if (sock_fd < 0)
return -1;
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* self pid */
printf("%d\n",getpid());
.....
When I set NETLINK_USER = 17,18 or something else, then I exec this code :
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
it show sock_fd return -1, so it must set NETLINK_USER = 16, I want know why?
And I have another question: Here is my kernel code:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <linux/semaphore.h>
#include <asm/cacheflush.h>
#include <linux/string.h>
#include <net/sock.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <stddef.h>
#define NETLINK_USER 16
struct sock *nl_sk = NULL;
void **sys_call_table;
asmlinkage int (*original_call_open) (const char*, int, int);
asmlinkage int (*original_call_read) (unsigned int, char*, int);
asmlinkage long (*sys_openat) (int, const char*, int, int);
asmlinkage long our_openat(int dfd, const char *filename, int flags, int mode){
printk("%s\n",filename);
return sys_openat(dfd,filename,flags,mode);
}
void hello_nl_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int pid;
struct sk_buff *skb_out;
int msg_size;
char *msg = "Hello from kernel";
int res;
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
msg_size = strlen(msg);
nlh = (struct nlmsghdr *)skb->data;
printk(KERN_INFO "Netlink received msg payload:%s\n", (char *)nlmsg_data(nlh));
pid = nlh->nlmsg_pid; //pid of sending process
skb_out = nlmsg_new(msg_size, 0);
if (!skb_out)
{
printk(KERN_ERR "Failed to allocate new skb\n");
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
NETLINK_CB(skb_out).dst_group = 0; //not in mcast group
strncpy(nlmsg_data(nlh), msg, msg_size);
res = nlmsg_unicast(nl_sk, skb_out, pid);
if (res < 0)
printk(KERN_INFO "Error while sending bak to user\n");
}
int init_module()
{
printk("Entering: %s\n", __FUNCTION__);
nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
// printk("%s",nl_sk);
// nl_sk = netlink_kernel_create(NETLINK_USER, input);
if (!nl_sk)
{
printk(KERN_ALERT "Error creating socket.\n");
return -10;
}
return 0;
}
void cleanup_module()
{
// Restore the original call
sys_call_table[__NR_open] = original_call_open;
sys_call_table[__NR_read] = original_call_read;
sys_call_table[__NR_openat] = sys_openat;
printk(KERN_INFO "exiting hello module\n");
netlink_kernel_release(nl_sk);
}
//MODULE_LICENSE("GPL");
//module_init(init_module);
//module_exit(cleanup_module);
I found if I set NETLINK_USER = 16, when I insert kernel, the avd will stop running, but if I set NETLINK_USER = 31,28 or something else the avd will run normally,I want to know why it will like this?
At last, I think the function hello_nl_recv_msg haven't exec, I don't know why.

Android Linux kernel communicate with user space

I'm following this article to implement android linux kernel communicate with user space.
I use insmod kernel to insert my kernel to Android avd goldfish kernel, then I use cat /proc/kmsg to observe the kernel message, but I find the program doesn't execute as intended. Here is my code:
void hello_nl_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
int pid;
struct sk_buff *skb_out;
int msg_size;
char *msg = "Hello from kernel";
int res;
printk(KERN_INFO "Entering: %sn", __FUNCTION__);
msg_size = strlen(msg);
nlh = (struct nlmsghdr *)skb->data;
printk(KERN_INFO "Netlink received msg payload:%sn", (char *)nlmsg_data(nlh));
pid = nlh->nlmsg_pid; //pid of sending process
skb_out = nlmsg_new(msg_size, 0);
if (!skb_out)
{
printk(KERN_ERR "Failed to allocate new skbn");
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
NETLINK_CB(skb_out).dst_group = 0; //not in mcast group
strncpy(nlmsg_data(nlh), msg, msg_size);
res = nlmsg_unicast(nl_sk, skb_out, pid);
if (res < 0)
printk(KERN_INFO "Error while sending bak to usern");
}
int init_module()
{
printk("Entering: %sn", __FUNCTION__);
nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
printk("%s",nl_sk);
// nl_sk = netlink_kernel_create(NETLINK_USER, input);
//if (!nl_sk)
//{
// printk(KERN_ALERT "Error creating socket.n");
// return -10;
//}
return 0;
}
I find when the program exec
nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
The kernel returns -1 or other integer, and it can't execute the function "hello_ne_recv_msg". I use android avd, the kernel is goldfish 2.6. Please help me, thanks.
Here is my code (tested on sony z2 kernel).
Note: your code may still fail if seandroid (selinux) is enforced.
Beware of code copy pasting. Check it!
Don't ignore compilation warnings.
Kernel module:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <net/sock.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
static struct sock* nl_sk;
#define NETLINK_USER 31
void hello_nl_recv_msg(struct sk_buff* skb)
{
struct nlmsghdr* nlh;
int pid;
struct sk_buff* skb_out;
int msg_size;
char* msg = "Hello from kernel";
int res;
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
msg_size = strlen(msg);
nlh = (struct nlmsghdr*)skb->data;
printk(KERN_INFO "Netlink received msg payload:%s\n", (char*)nlmsg_data(nlh));
pid = nlh->nlmsg_pid; //pid of sending process
skb_out = nlmsg_new(msg_size, 0);
if (!skb_out) {
printk(KERN_ERR "Failed to allocate new skbn\n");
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
NETLINK_CB(skb_out).dst_group = 0; //not in mcast group
strncpy(nlmsg_data(nlh), msg, msg_size);
res = nlmsg_unicast(nl_sk, skb_out, pid);
if (res < 0) {
printk(KERN_INFO "Error while sending back to user\n");
}
}
int __init init_netlink_test(void)
{
printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, 0, hello_nl_recv_msg, NULL, THIS_MODULE);
if (!nl_sk) {
printk(KERN_ALERT "Error creating socket.\n");
return -10;
}
return 0;
}
void __exit exit_netlink_test(void)
{
printk(KERN_INFO "exiting hello module\n");
netlink_kernel_release(nl_sk);
}
module_init(init_netlink_test);
module_exit(exit_netlink_test);
userspace test app:
#include <sys/socket.h>
#include <linux/netlink.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#define NETLINK_USER 31
#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr* nlh = NULL;
struct iovec iov;
int sock_fd;
struct msghdr msg;
int main()
{
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
if (sock_fd < 0) {
return -1;
}
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* self pid */
/* interested in group 1<<0 */
bind(sock_fd, (struct sockaddr*)&src_addr,
sizeof(src_addr));
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* For Linux Kernel */
dest_addr.nl_groups = 0; /* unicast */
nlh = (struct nlmsghdr*)malloc(
NLMSG_SPACE(MAX_PAYLOAD));
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = 0;
strcpy(NLMSG_DATA(nlh), "Hello");
iov.iov_base = (void*)nlh;
iov.iov_len = nlh->nlmsg_len;
msg.msg_name = (void*)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
printf("Sending message to kernel\n");
sendmsg(sock_fd, &msg, 0);
printf("Waiting for message from kernel\n");
/* Read message from kernel */
recvmsg(sock_fd, &msg, 0);
printf(" Received message payload: %s\n",
NLMSG_DATA(nlh));
close(sock_fd);
}

ld returned 1 exit status - c

Im trying to compile reboot.c binary for android but i keep getting following error:
/home/pedja/android-ndk-r8d/toolchains/x86-4.6/prebuilt/linux-x86/bin/../lib/gcc/i686- linux-android/4.6/../../../../i686-linux-android/bin/ld: ./obj/local/x86/objs/reboot /reboot.o: in function main:jni/reboot.c:49: error: undefined reference to 'android_reboot'
/home/pedja/android-ndk-r8d/toolchains/x86-4.6/prebuilt/linux-x86/bin/../lib/gcc/i686-linux-android/4.6/../../../../i686-linux-android/bin/ld: ./obj/local/x86/objs/reboot /reboot.o: in function main:jni/reboot.c:51: error: undefined reference to 'android_reboot'
/home/pedja/android-ndk-r8d/toolchains/x86-4.6/prebuilt/linux-x86/bin/../lib/gcc/i686-linux-android/4.6/../../../../i686-linux-android/bin/ld: ./obj/local/x86/objs/reboot/reboot.o: in function main:jni/reboot.c:47: error: undefined reference to 'android_reboot'
collect2: ld returned 1 exit status
make: *** [obj/local/x86/reboot] Error 1
reboot.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "android_reboot.h"
#include <unistd.h>
int main(int argc, char *argv[])
{
int ret;
int nosync = 0;
int poweroff = 0;
int flags = 0;
opterr = 0;
do {
int c;
c = getopt(argc, argv, "np");
if (c == EOF) {
break;
}
switch (c) {
case 'n':
nosync = 1;
break;
case 'p':
poweroff = 1;
break;
case '?':
fprintf(stderr, "usage: %s [-n] [-p] [rebootcommand]\n", argv[0]);
exit(EXIT_FAILURE);
}
} while (1);
if(argc > optind + 1) {
fprintf(stderr, "%s: too many arguments\n", argv[0]);
exit(EXIT_FAILURE);
}
if(nosync)
/* also set NO_REMOUNT_RO as remount ro includes an implicit sync */
flags = ANDROID_RB_FLAG_NO_SYNC | ANDROID_RB_FLAG_NO_REMOUNT_RO;
if(poweroff)
ret = android_reboot(ANDROID_RB_POWEROFF, flags, 0);
else if(argc > optind)
ret = android_reboot(ANDROID_RB_RESTART2, flags, argv[optind]);
else
ret = android_reboot(ANDROID_RB_RESTART, flags, 0);
if(ret < 0) {
perror("reboot");
exit(EXIT_FAILURE);
}
fprintf(stderr, "reboot returned\n");
return 0;
}
android_reboot.h
#ifndef __ANDROID_REBOOT_H__
#define __ANDROID_REBOOT_H__
__BEGIN_DECLS
/* Commands */
#define ANDROID_RB_RESTART 0xDEAD0001
#define ANDROID_RB_POWEROFF 0xDEAD0002
#define ANDROID_RB_RESTART2 0xDEAD0003
/* Flags */
#define ANDROID_RB_FLAG_NO_SYNC 0x1
#define ANDROID_RB_FLAG_NO_REMOUNT_RO 0x2
int android_reboot(int cmd, int flags, char *arg);
__END_DECLS
#endif
You're showing that you've declared android_reboot however your link fails because the implementation of that function cannot be found. Declaration is not the same as definition... declaration satisfies the compiler, but only implementation satisfies the linker. Where is this function implemented?

Qt/Necessitas - reasonable QFileDialog replacement/skin?

I'm looking for a nice way to address porting Qt applications to Qt/Necessitas (Android).
Some of the QtGUI widgets are absolutely atrocious - unfortunately, including QFileDialog.
Do you know of any replacements with a proper look and feel?
Is making QFileDialog usable anywhere near high priority for Necessitas developers?
#include <QApplication>
#include <QFileDialog>
int main(int argc, char* argv[]) {
QApplication a(argc, argv);
QString fileName = QFileDialog::getOpenFileName(NULL,
QObject::tr("Open Image"), "/home/jana", QObject::tr("Image Files (*.png *.jpg *.bmp)"));
a.exec();
}
Android does not have own, native file dialog. We can use QtAndroidExtras to invoke external application which is able to pick a file:
I wrote wrapper, that could be used for that. Here's full code:
androidfiledialog.h
#ifndef ANDROIDFILEDIALOG_H
#define ANDROIDFILEDIALOG_H
#include <QObject>
#include <QAndroidJniObject>
#include <QtAndroid>
#include <QAndroidActivityResultReceiver>
class AndroidFileDialog : public QObject
{
Q_OBJECT
public:
explicit AndroidFileDialog(QObject *parent = 0);
virtual ~AndroidFileDialog();
bool provideExistingFileName();
private:
class ResultReceiver : public QAndroidActivityResultReceiver {
AndroidFileDialog *_dialog;
public:
ResultReceiver(AndroidFileDialog *dialog);
virtual ~ResultReceiver();
void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data);
QString uriToPath(QAndroidJniObject uri);
};
static const int EXISTING_FILE_NAME_REQUEST = 1;
ResultReceiver *receiver;
void emitExistingFileNameReady(QString result);
signals:
void existingFileNameReady(QString result);
};
#endif // ANDROIDFILEDIALOG_H
androidfiledialog.cpp
#include "androidfiledialog.h"
AndroidFileDialog::ResultReceiver::ResultReceiver(AndroidFileDialog *dialog) : _dialog(dialog) {}
AndroidFileDialog::ResultReceiver::~ResultReceiver() {}
void AndroidFileDialog::ResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
{
jint RESULT_OK = QAndroidJniObject::getStaticField<jint>("android/app/Activity", "RESULT_OK");
if (receiverRequestCode == EXISTING_FILE_NAME_REQUEST && resultCode == RESULT_OK) {
QAndroidJniObject uri = data.callObjectMethod("getData", "()Landroid/net/Uri;");
QString path = uriToPath(uri);
_dialog->emitExistingFileNameReady(path);
} else {
_dialog->emitExistingFileNameReady(QString());
}
}
QString AndroidFileDialog::ResultReceiver::uriToPath(QAndroidJniObject uri)
{
if (uri.toString().startsWith("file:", Qt::CaseInsensitive)) {
return uri.callObjectMethod("getPath", "()Ljava/lang/String;").toString();
} else {
QAndroidJniObject contentResolver = QtAndroid::androidActivity().callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;");
QAndroidJniObject cursor = contentResolver.callObjectMethod("query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;", uri.object<jobject>(), 0, 0, 0, 0);
QAndroidJniObject DATA = QAndroidJniObject::fromString("_data");
jint columnIndex = cursor.callMethod<jint>("getColumnIndexOrThrow", "(Ljava/lang/String;)I", DATA.object<jstring>());
cursor.callMethod<jboolean>("moveToFirst", "()Z");
QAndroidJniObject result = cursor.callObjectMethod("getString", "(I)Ljava/lang/String;", columnIndex);
return result.isValid() ? result.toString() : QString();
}
}
AndroidFileDialog::AndroidFileDialog(QObject *parent) : QObject(parent)
{
receiver = new ResultReceiver(this);
}
AndroidFileDialog::~AndroidFileDialog()
{
delete receiver;
}
bool AndroidFileDialog::provideExistingFileName()
{
QAndroidJniObject ACTION_GET_CONTENT = QAndroidJniObject::fromString("android.intent.action.GET_CONTENT");
QAndroidJniObject intent("android/content/Intent");
if (ACTION_GET_CONTENT.isValid() && intent.isValid()) {
intent.callObjectMethod("setAction", "(Ljava/lang/String;)Landroid/content/Intent;", ACTION_GET_CONTENT.object<jstring>());
intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", QAndroidJniObject::fromString("file/*").object<jstring>());
QtAndroid::startActivity(intent.object<jobject>(), EXISTING_FILE_NAME_REQUEST, receiver);
return true;
} else {
return false;
}
}
void AndroidFileDialog::emitExistingFileNameReady(QString result)
{
emit existingFileNameReady(result);
}
You have to add to your *.pro file:
QT += androidextras
using example:
AndroidFileDialog *fileDialog = new AndroidFileDialog();
connect(fileDialog, SIGNAL(existingFileNameReady(QString)), this, SLOT(openFileNameReady(QString)));
bool success = fileDialog->provideExistingFileName();
if (!success) {
qDebug() << "Problem with JNI or sth like that...";
disconnect(fileDialog, SIGNAL(existingFileNameReady(QString)), this, SLOT(openFileNameReady(QString)));
//or just delete fileDialog instead of disconnect
}
slot implementation:
void MyClass::openFileNameReady(QString fileName)
{
if (!fileName.isNull()) {
qDebug() << "FileName: " << fileName;
} else {
qDebug() << "User did not choose file";
}
}
Please confirm this solution works properly on your device.
You could easily build your own file dialog either with QtWidgets or QML, by using the out-of-the-box QFileSystemModel class or the FolderListModel element.
As for whether it is priority or not, at this point it seems that Necessitas will be absorbed by Digia's efforts to support Android. I doubt there will be significant efforts to style QtWidgets appropriately, since the module is marked as DONE and all the focus for UI is on QML. So, I wouldn't hold by breath if I were you. Plus the stock Qt widgets look completely ugly on non-desktop platforms.

Categories

Resources