东方欲晓 @ 2005-06-13 20:52
深入BREW模块加载机制
作者:东方欲晓
2005-6-13
MSN:[email protected]
Email: [email protected]
转载请注明出处
在BREW中,module是基本的执行单位,一个module可以包含一个或多个applet,或者多个extension class。按照module处于code space(即OEM出厂时已经将module编译进image中了)还是通过下载方式(无线下载或者数据线下载)存于文件系统可以分为static和dynamic,主要包括:dynamic module(applet),static module(applet)和dynamic extension class(module)。本文将详细阐述static和dynamic module的加载过程。由于完整的module加载过程高通没有文档公开,所以以下内容是结合我阅读的相关文档以及我的日常工作而写,是我对模块加载的理解,可能有不当之处,还请指教。
1. Module的信息
MIF文件,这个大家都知道了。原则上来说,每个module都需要有标识自身的MIF文件,从BREW 3.1开始已经强制如此了,static module也需要有相应的MIF。而在BREW3.1之前,对于static module是没有单独的MIF文件的,但有一个AEEAppInfo的结构体来表示module的信息,里面主要包括clsid,app type等信息,每个static module都需要有一个实例化的AEEAppInfo结构体,BREW从此结构中获得必要的module信息。
2. 枚举Module信息
这一步是在BREW环境初始化的时候进行的(猜测是在AEE_init中),由于通常在开机时就初始化BREW环境,所以枚举Module信息通过在一开机就进行(进行程序跟踪结果也证实如此)。对于dynamic module,BREW通过检索各个MIF文件来获得各个module的必要信息,比如clsid等。而对于static module,由于不存在MIF文件,所以过程有所不同。每个static module的实现必须提供一个XXX_getmodinfo(),在该函数中返回特定于该module的Mod_Load()函数指针,通常形式为 XXXMod_Load,同时返回特定于该module的AEEAppInfo结构数据。所有的这些XXX_getmodinfo函数指针构成了一个staticmodinfo的数组。BREW初始化时通过检索该数组(猜测执行其中的每一个函数)来获得每个static module的相关模块信息(比如clsid)以及加载函数(我们知道,对于dynamic module, load的函数是通用的AEEMod_Load,所以应用无需再提供自己的Load函数,而static module必须提供自己的Mod_Load 函数)
3. Module加载
Module的加载是在运行时才进行的,即执行该Module的时候。对于dynamic module,加载是通过通用函数AEEMod_Load实现的,而AEEMod_Load实际是调用AEEStaticMod_New,该函数的原型为int AEEStaticMod_New(int16 nSize, IShell *pIShell, void *ph, IModule **ppMod, PFNMODCREATEINST pfnMC,PFNFREEMODDATA pfnMF),大家在AeeModGen.c中可以看到。需要说明的是,AEEMod_Load中调用该函数时,倒数第二个参数为NULL,这说明什么?我们来看看AEEStaticMod_New具体作些什么,
int AEEStaticMod_New(int16 nSize, IShell *pIShell, void *ph, IModule **ppMod,
PFNMODCREATEINST pfnMC,PFNFREEMODDATA pfnMF)
JAVA手机网[www.cnjm.net]
{
AEEMod *pMe = NULL;
VTBL(IModule) *modFuncs;
if (!ppMod || !pIShell) {
return EFAILED;
}
*ppMod = NULL;
#ifdef AEE_SIMULATOR
// IMPORTANT NOTE: g_pvtAEEStdLibEntry global variable is defined for
// SDK ONLY! This variable should NOT BE:
//
// (1) overwritten
// (2) USED DIRECTLY by BREW SDK users.
JAVA手机网[www.cnjm.net]
//
// g_pvtAEEStdLibEntry is used as an entry point to AEEStdLib,
// DO NOT REMOVE the next five lines.
if (!ph) {
return EFAILED;
} else {
g_pvtAEEStdLibEntry = (AEEHelperFuncs *)ph;
}
#endif
//Allocate memory for the AEEMod object
if (nSize < sizeof(AEEMod)) {
nSize += sizeof(AEEMod);
JAVA手机网[www.cnjm.net]
}
if (NULL == (pMe = (AEEMod *)MALLOC(nSize + sizeof(IModuleVtbl)))) {
return ENOMEMORY;
}
JAVA手机网[www.cnjm.net]
// Allocate the vtbl and initialize it. Note that the modules and apps
JAVA手机网[www.cnjm.net]
// must not have any static data. Hence, we need to allocate the vtbl as
// well.
modFuncs = (IModuleVtbl *)((byte *)pMe + nSize);
JAVA手机网[www.cnjm.net]
// Initialize individual entries in the VTBL
modFuncs->AddRef = AEEMod_AddRef;
modFuncs->Release = AEEMod_Release;
modFuncs->CreateInstance = AEEMod_CreateInstance;
modFuncs->FreeResources = AEEMod_FreeResources;
// initialize the vtable
JAVA手机网[www.cnjm.net]
INIT_VTBL(pMe, IModule, *modFuncs);
// initialize the data members
// Store address of Module's CreateInstance function
pMe->pfnModCrInst = pfnMC;
JAVA手机网[www.cnjm.net]
// Store Address of Module's FreeData function
pMe->pfnModFreeData = pfnMF;
JAVA手机网[www.cnjm.net]
pMe->m_nRefs = 1;
pMe->m_pIShell = pIShell;
// Set the pointer in the parameter
*ppMod = (IModule*)pMe;
return SUCCESS;
上述代码在sdk中的AeeModGen.c可以找到,概括起来,就是在为module分配内存,并且实例化vtbl表,其中有两行代码值得注意:
modFuncs->CreateInstance = AEEMod_CreateInstance;
pMe->pfnModCrInst = pfnMC;
第一行是指定module的创建函数为AEEMod_CreateInstance,而第二行是指定该module具有自身特殊的创建函数,该函数即为参数pfnMC指定的函数。而在AEEMod_Load中调用AEEStaticMod_New时该参数为NULL,即所有dynamic module采用通用的createinstance 函数(该函数实际上即为AEEClsCreateInstance)
大家或许已经猜到了,对于static module,其实其自身的XXXMod_Load加载函数和通用的AEEMod_Load具体实现几乎一样,最主要的区别在于其调用AEEStaticMod_New时指定了pfnMC参数,即static module需要指定自身的createinstance函数。
4. Module 创建
这是通过 在AEEStaticMod_New中代码
modFuncs->CreateInstance = AEEMod_CreateInstance;指定的
JAVA手机网[www.cnjm.net]
AEEMod_CreateInstance函数来创建的。我们来看一下AEEMod_CreateInstance的庐山真面目:
static int AEEMod_CreateInstance(IModule *pIModule,IShell *pIShell,
AEECLSID ClsId,void **ppObj)
{
AEEMod *pme = (AEEMod *)pIModule;
int nErr = 0;
// For a dynamic module, they must supply the AEEClsCreateInstance()
// function. Hence, invoke it. For a static app, they will have
// registered the create Instance function. Invoke it.
JAVA手机网[www.cnjm.net]
if (pme->pfnModCrInst) {
nErr = pme->pfnModCrInst(ClsId, pIShell, pIModule, ppObj);
#if !defined(AEE_STATIC)
} else {
nErr = AEEClsCreateInstance(ClsId, pIShell, pIModule, ppObj);
JAVA手机网[www.cnjm.net]
#endif
JAVA手机网[www.cnjm.net]
}
return nErr;
}
可见对于dynamic module,由于pme->pfnModCrInst为NULL,所以调用通用的创建函数AEEClsCreateInstance(ClsId, pIShell, pIModule, ppObj)来进行创建。而对于static module,因为指定了自身的Createinstance函数,所以pme->pfnModCrInst不为NULL,从而执行特定于该module自身的createinstance函数
JAVA手机网[www.cnjm.net]
5. Applet创建
之后,不管是 AEEClsCreateInstance还是pme->pfnModCrInst其实都是类似的,通过AEEApplet_New(除extension class外通常都是调用该函数)来最终创建applet,AEEApplet_New无非是具体分配applet内存,初始化applet的vtbl(这里最重要的是实例化applet_handleevent)并返回Iapplet指针,供运行时使用
JAVA手机网[www.cnjm.net]
6. 程序运行
通常就是在applet_handleevent中进行各种事件处理。处理前一般将传入的Iapplet指针先转换为AEEApplet或者用户自身的结构,以便可以访问其中的数据变量。