cuijpus的专栏

关注范围:Telecommunication, Mobile , Embedded Linux。好记性,不如烂笔头。

用户操作
[即时聊天] [发私信] [加为好友]
崔计平ID:cuijpus
51285次访问,排名2233(-1),好友22人,关注者49人。
做手机研发5年多,涉及app, middleware, driver等;好记性不如烂笔头,随手写些,免得忘记了。
cuijpus的文章
原创 155 篇
翻译 0 篇
转载 47 篇
评论 56 篇
cuijpus的公告
目前正在分析研究7个Linux手机平台的异同点


最近评论
qiuyu:您好,我想问一下是不是第三方开发只需LiMo的应用程序编程接口,在此基础上编程就可以了?我看了一下我得linux平台下并没有这个API的实现程序,这样的话只根据接口函数就能使用?一般情况下的话是不是得需要个库之类的文件呀?我是新手,请赐教!
yuhang111:我想还是兼容性的问题,maemo的不少组件是基于debian开发的, 所有应该更方便的在ubuntu上使用吧。当然用fedora也可以,好像虚拟机下用fedora要比ubuntu慢些吧。
wei04:请问D-BUS的C API是线程安全的么?
Liyonn8744:您好,Maemo平台的宿主Linux一定要用ubuntu的吗?用Fedora Core 6可以吗?有什么区别呢?谢谢~
SearchSun:同期待,如果有一个通过libosso封装的D-BUS来调用media player的例子就好了
文章分类
收藏
相册
常去的网站
linux mobile research圈子
存档
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes

原创 Maemo Linux手机平台系列分析:7 Maemo平台开发之LibOSSO收藏

新一篇: Maemo Linux手机平台系列分析:8 Maemo平台开发之 使用Glib绑定的D-Bus  | 旧一篇: Maemo Linux手机平台系列分析:6 Maemo平台开发之D-Bus

 
这部分的内容:
  • LibOSSO介绍
  • 调用LibOSSO去实现D-Bus方法调用
  • 调用LibOSSO实现异步方法调用
  • 设备状态和模式的通知
  • 模拟设备状态的改变
 
LibOSSO介绍
几乎Maemo中所有的程序都要使用LibOSSO 库。主要的原因是:LibOSSO可以保护一些将被杀掉的进程幸免于难,而不被杀掉。当从任务导航栏中启动一个没有注册D-Bus服务的程序时,超过一定时间,这个程序将由桌面环境去杀掉。LibOSSO另外一个方面的功能是版本隔离作用:由于D-Bus仍然在开发升级中,由LibOSSOD-Bus进行封装,用户程序只要调用LibOSSO的对应接口就行,不必去直接调用D-Bus接口。
除了保护和隔离作用外,LibOSSO还提供了另外一些有用的功能:自动保存和状态保存;处理硬件状态;以及其它重要的事件。还有:对D-Bus的封装可以使得LibOSSO提供非常方便的函数去做RPC(远程调用).
下面我们会重点介绍LibOSSORPC
 
通过LibOSSO库实现D-Bus的方法调用
我们从哪开始呢?记得我们前面曾直接使用libdbus的函数实现了一个RPC, 这里我们依然使用这个例子,只不过,这次我们用LibOSSO的函数去实现一个同样弹出对话框的功能。当然,LibOSSO库中有个函数(osso_system_note_dialog)可以直接去实现这种功能. 我们暂时不用它。我们通过详细的代码看看LibOSSO如何支持RPC的。
头文件:
#include <libosso.h> //必须包含这个头文件
 
 /*... Listing cut for brevity ...*/
//与上面一个例子同样的定义
#define SYSNOTE_NAME "org.freedesktop.Notifications"
#define SYSNOTE_OPATH "/org/freedesktop/Notifications"
#define SYSNOTE_IFACE "org.freedesktop.Notifications"
#define SYSNOTE_NOTE "SystemNoteDialog"
[ 使用LibOSSO库函数需要包含的头文件 ]
使用LibOSSO库,仅仅需要包含一个libosso.h头文件,我们这里同样定义D-Bus service名字,对象路径、接口名字、方法名字;
我们看看LibOSSO的上下文如何创建的以及如何运行的:
int main(int argc, char** argv) {
 
 /* The LibOSSO context that we need to do RPC. */
 osso_context_t* ossoContext = NULL;
 
 g_print("Initializing LibOSSO\n");
 /* The program name for registration is communicated from the
    Makefile via a -D preprocessor directive. Since it doesn't
     contain any dots in it, a prefix of "com.nokia." will be added
     to it internally within osso_initialize(). */
 ossoContext = osso_initialize(ProgName, "1.0", FALSE, NULL);
 if (ossoContext == NULL) {
    g_error("Failed to initialize LibOSSO\n");
 }
 
 g_print("Invoking the method call\n");
 runRPC(ossoContext);
 
 g_print("Shutting down LibOSSO\n");
 /* Deinitialize LibOSSO. The function doesn't return status code so
     we cannot know whether it succeeded or failed. We assume that it
     always succeeds. */
 osso_deinitialize(ossoContext);
 ossoContext = NULL;
 
 g_print("Quitting\n");
 return EXIT_SUCCESS;
}
[LibOSSO 初始化与卸载,见(libosso-example-sync/libosso-rpc-sync.c) ]
LibOSSO上下文,这个数据结构含有通过D-Bus进行通信的必要信息。当准备创建这个上下文时,第一个参数:你需要传入一个程序名字给osso_initialize, 这个名字有两个用途:1,注册给D-Bus; 2, 将来阻止销毁任务杀掉你的进程。如果这个名字没有包含任何点字符,那么,LibOSSO会自动把那个名字加个前缀:com.nokia. 一般情况下,用户是看不到这个名字的,不过,这问题不大。如果你老是使用这种没有前缀的名字的话,可能会和别人的程序名冲突。建议使用全名。
第二个参数:版本号,目前LibOSSO中并没有用,不过你可以加上,以后可能会用的。
倒数第二个参数:如果最后一个参数传入NULL,这个参数不会起到任何作用。最后一个参数:使用LibOSSO自己的GMainLoop还是外面的,如果传入NULL,则直接使用LibOSSO内部的主循环。
释放LibOSSO 上下文会自动关闭链接到D-Bus上面的链接通道,同时释放一些分配的内存。
下面这一段代码演示了如何使用LibOSSORPC调用,以及错误处理。
/**
 * Do the RPC call.(实现D-Bus远程调用的示例)
 *
 * Note that this function will block until the method call either
 * succeeds, or fails. If the method call would take a long time to
 * run, this would block the GUI of the program (which we don't have).
 * ()
 * Needs the LibOSSO state to do the launch.
 */
static void runRPC(osso_context_t* ctx) {
 
 /* Message to display. */
 const char* dispMsg = "Hello SystemNote!\nVia LibOSSO/sync.";
 /* Icon type to use. */
 gint iconType = OSSO_GN_ERROR;
 /* Button label text to use, "" means leaving the defaults. */
 const char* labelText = "";
 
 /* Will hold the result from the RPC invocation function. */
 osso_return_t result;
 /* Will hold the result of the method call (or error). */
 osso_rpc_t methodResult = {};
 
 g_print("runRPC called\n");
 
 g_assert(ctx != NULL);
 
 /* Compared to the libdbus functions, LibOSSO provides conveniently
     a function that will do the dispatch and also allows us to pass
     the arguments all with one call. (libOSSO比libdbus更简单
 
     The arguments for the "SystemNoteDialog" are the same as in
     dbus-example.c (since it is the same service). You might also
     notice that even if LibOSSO provides some convenience, it does
     not completely isolate us from libdbus. We still supply the
     argument types using D-Bus constants. (在传参数方面,LibOSSO并没有完全同D-Bus隔离,还是使用D-Bus的参数规定)
 
     NOTE Do not pass the argument values by pointers as with libdbus,
          instead pass them by value (as below). */
/* (这是个阻塞调用,如果失败的话,可能会花费很长时间才能返回,这取决于D-Bus的超时时间值,D-Bus同步调用的超时时间值一般是25s)*/
 result = osso_rpc_run(ctx,
                        SYSNOTE_NAME,      /* well-known name */
                        SYSNOTE_OPATH,         /* object path */
                        SYSNOTE_IFACE,           /* interface */
                        SYSNOTE_NOTE,          /* method name */
                        &methodResult, /* method return value */
                        /* The arguments for the RPC. The types
                           are unchanged, but instead of passing
                           them via pointers, they're passed by
                           "value" instead. */
                        DBUS_TYPE_STRING, dispMsg,
                        DBUS_TYPE_UINT32, iconType,
                        DBUS_TYPE_STRING, labelText,
                        DBUS_TYPE_INVALID);
 /* Check whether launching the RPC succeeded. */
 if (result != OSSO_OK) {
    g_error("Error launching the RPC (%s)\n",
            ossoErrorStr(result));
    /* We also terminate right away since there's nothing to do. */
 }
 g_print("RPC launched successfully\n");
 
 /* Now decode the return data from the method call.
     NOTE: If there is an error during RPC delivery, the return value
           will be a string. It is not possible to differentiate that
           condition from an RPC call that returns a string.
 
     If a method returns "void", the type-field in the methodResult
     will be set to DBUS_TYPE_INVALID. This is not an error. */
 g_print("Method returns: ");
 printOssoValue(&methodResult); /* 查看方法调用的返回情况 */
 g_print("\n");
 
 g_print("runRPC ending\n");
}
大家一定要注意:函数osso_rpc_run是个同步(阻塞)调用,有两种结果:有个及时的相应;或者超时返回。在实际使用过程中,一定要铭记这是一个阻塞的调用。另外呢:LibOSSO也有一套异步(非阻塞)的RPC函数。
如果你的方法调用返回不止一个值(D-Bus是支持的),不过,LibOSSO 目前并不支持返回所有的(而是返回第一个)。
对于函数返回码的Error code,这里非常直接的。我有篇文章是专门讲如何做Error code, 那篇文章中对于Error code的解析非常好:有巧妙的、有直白的,也有更专业的。
/**
 * Utility to return a pointer to a statically allocated string giving
 * the textural representation of LibOSSO errors. Has no internal
 * state (safe to use from threads).
 *
 * LibOSSO does not come with a function for this, so we define one
 * ourselves.
 */
static const gchar* ossoErrorStr(osso_return_t errCode) {
 
 switch (errCode) {
    case OSSO_OK:
      return "No error (OSSO_OK)";
    case OSSO_ERROR:
      return "Some kind of error occurred (OSSO_ERROR)";
    case OSSO_INVALID:
      return "At least one parameter is invalid (OSSO_INVALID)";
    case OSSO_RPC_ERROR:
      return "Osso RPC method returned an error (OSSO_RPC_ERROR)";
    case OSSO_ERROR_NAME:
      return "(undocumented error) (OSSO_ERROR_NAME)";
    case OSSO_ERROR_NO_STATE:
      return "No state file found to read (OSSO_ERROR_NO_STATE)";
    case OSSO_ERROR_STATE_SIZE:
      return "Size of state file unexpected (OSSO_ERROR_STATE_SIZE)";
    default:
      return "Unknown/Undefined";
 }
}
对于函数返回值的解析,有点复杂,因为返回值是包含在一个结构中的,需要逐个分拣。
/**
 * Utility to print out the type and content of given osso_rpc_t.
 * It also demonstrates the types available when using LibOSSO for
 * the RPC. Most simple types are available, but arrays are not
 * (unfortunately).
 */
static void printOssoValue(const osso_rpc_t* val) {
 
 g_assert(val != NULL);
 
 // 对返回值的类型进行分拣,还是D-Bus的特色,让人用起来不习惯
 switch (val->type) {
    case DBUS_TYPE_BOOLEAN:
      g_print("boolean:%s", (val->value.b == TRUE)?"TRUE":"FALSE");
      break;
    case DBUS_TYPE_DOUBLE:
      g_print("double:%.3f", val->value.d);
      break;
    case DBUS_TYPE_INT32:
      g_print("int32:%d", val->value.i);
      break;
    case DBUS_TYPE_UINT32:
      g_print("uint32:%u", val->value.u);
      break;
    case DBUS_TYPE_STRING:
      g_print("string:'%s'", val->value.s);
      break;
    case DBUS_TYPE_INVALID:
      g_print("invalid/void");
      break;
    default:
      g_print("unknown(type=%d)", val->type);
      break;
 }
}
请注意:libOSSO RPC函数并不支持数组参数,因此你要使用一些简单的参数了L。这可是不好的消息,GArray不能在libOSSO 中使用了?D-Bus可是支持的啊。估计是支持的,有时间试探试探。
把上面的例子编译后运行,我们发现会弹出一个熟悉的对话框。
[sbox-CHINOOK_X86: ~/libosso-example-sync] > run-standalone.sh ./libosso-rpc-sync
Initializing LibOSSO
Invoking the method call
runRPC called
/dev/dsp: No such file or directory
RPC launched successfully
Method returns: uint32:8
runRPC ending
Shutting down LibOSSO
Quitting
和前一个例子的区别就是,多了一个关于音频设备的错误信息。这个信息可能会在RPC返回之前出现,因为runRPC是阻塞调用。
 
我们在前面的代码中用到了一个程序名称:ProgName,我们一般把它定义在makefile文件中,为什么呢?有时候我们需要分别编译i386ARMEL,如果定义在头文件中,可能需要区分定义,如果放在makefile总定义,由于makefile的开头一般会先区分i386,ARM另外一个好处是,我们把ProgName定义在makefile中,可以提高上面代码的复用率。
# define a list of pkg-config packages we want to use
pkg_packages := glib-2.0 libosso
 
# ... Listing cut for brevity ...
 
libosso-rpc-sync: libosso-rpc-sync.c
      $(CC) $(CFLAGS) -DProgName=\"LibOSSOExample\" \ #定义在这里好处有两个,1 提高代码重用性,2 方便为不同的archetecture定义
       $< -o $@ $(LDFLAGS)
 
LibOSSO的异步调用
有时候,一个函数需要很长时间才能执行完,或者你不能确定这个函数是否需要很长时间,这些情况下,你应当使用异步调用,而不要使用同步调用。同步和异步最大的区别就是:异步是把同步割成两部分,启动RPC和在回调中处理返回结果。就是说,你RPC什么时候回来,我什么时候处理你的结果,这样就不至于导致D-Bus的超时问题。
为了使用LibOSSO的回调和控制其主循环,我们需要创建一个状态变量,这个状态变量在需要时,会传递给回调。
/**
 * Small application state so that we can pass both LibOSSO context
 * and the mainloop around to the callbacks.
 */
typedef struct {
 /* A mainloop object that will "drive" our example. */
 GMainLoop* mainloop;
  /* The LibOSSO context which we use to do RPC. */
 osso_context_t* ossoContext;
} ApplicationState;
函数osso_rpc_async_run是用来启动异步调用的,并且它立即返回。如果它返回了一个错误,可能是client端的错误,而不是server端得错误,因为RPC的错误没有通过它返回。那么,谁来处理RPC的回应数据呢,回调函数!
/**
 * We launch the RPC call from within a timer callback in order to
 * make sure that a mainloop object will be running when the RPC will
 * return (to avoid a nasty race condition).
 *
 * So, in essence this is a one-shot timer callback.
 *
 * In order to launch the RPC, it will need to get a valid LibOSSO
 * context (which is carried via the userData/application state
 * parameter).
 */
static gboolean launchRPC(gpointer userData) {
 
 ApplicationState* state = (ApplicationState*)userData;
 /* Message to display. */
 const char* dispMsg = "Hello SystemNote!\nVia LibOSSO/async.";
 /* Icon type to use. */
 gint iconType = OSSO_GN_ERROR;
 /* Button label text to use. */
 const char* labelText = "Execute!";
 
 /* Will hold the result from the RPC launch call. */
 osso_return_t result;
 
 g_print("launchRPC called\n");
 
 g_assert(state != NULL);
 
 /*... Listing cut for brevity ...*/
 
 /* The only difference compared to the synchronous version is the
     addition of the callback function parameter, and the user-data