Writing BREW Extensions

Writing BREW Extensions
By Ray Rischpater

 

Introduction

One of the key differences between QUALCOMM BREW and other smart phone operating systems is its component-oriented approach to software development. Nearly every API you use when writing a BREW application is actually part of one or more software components, individually packaged by QUALCOMM or the handset OEM as part of the BREW runtime. Like most component-oriented environments such as the Component Object Model (COM), it's possible for you to create your own component, called an extension, that looks and behaves just like any other BREW interface, such as IShell or IDisplay. The extension you create can contain one or more BREW classes, and can be private to your application, or public for other applications (either yours or the world at large) to use.

Why use the extension model, when things like C modules or classes provide ample opportunities for modularity? BREW extensions provide several advantages over classes or modules for major functional areas of your application such as data storage, protocols, or codecs, including:

You can make an extension that other companies can license and from which you can make money using the same model as with other BREW applications, giving you additional opportunities for revenue.
Extensions are shared code outside your application; the handset downloads only one copy of an extension's code regardless of how many applications use the extension, letting you save valuable space on the handset's file system.
Because extensions live outside your application, they're an ideal way for multiple teams at your firm to isolate development to specific functional areas.
Extensions follow the same paradigm as other BREW interfaces, making it easier for new team members (or customers using your public extensions) to learn how to use your libraries.
Unlike modules, classes, or even libraries, BREW extensions are served by the BREW Delivery System (BDS) when needed by an application. Thus, if your application relies on an extension, when a customer purchases your application, the extension is downloaded automatically. More importantly, if a customer purchases a second application that uses the same extension, it's not downloaded again. Network and storage use is minimized by the fact that the extension is a shared component between multiple applications, in the same way that dynamically linked libraries save memory and disk space under Linux, Windows, and other desktop and server operating systems.
Crafting a BREW Extension

What makes a good BREW extension? Almost anything you identify as a candidate for reuse between applications that doesn't have a user interface, including:

Codecs for image, audio, or video presentation. Ideally, these implement the BREW IMedia interface (or IImage for static images), so that your customers can treat your codec as if it were bundled with BREW.
Data storage for data shared between applications, such as personal information managers.
Protocol handlers for Internet- or SMS-based protocols.
New user interface components that extend the BREW UI Toolkit's widgets, or even the older BREW IControl based hierarchy of components.
Some successful BREW developers have gone as far as breaking the bulk of their applications up into shared extensions, so the actual application you see in fact simply leverages a handful of extensions shared among their products. Other companies, such as M7 Networks and Truvideo, provide extensions to application developers, bringing high score management, video playback, and other features to other companies' applications. Finally, many Original Equipment Manufacturers (OEMs) use BREW extensions under the hood when building the applications that make up a cell phone's user interface.

A BREW extension has at least three components: its Module Information File (MIF), a header declaring its public interface, and its private implementation. Users of your extension include only its public interface header, whereas when you provide your interface to other applications through the BDS, you're making both the MIF and the private implementation (compiled as a conventional BREW module, a .MOD file) available.

Clients of your extension create new instances of the classes it contains using ISHELL_ClassCreateInstance, just as they would any other BREW class. To ensure this linkage works correctly, it's important to define your extension's class IDs correctly in your extension's MIF (see Figure 1). To do this:

Launch the BREW MIF Editor.
Select the "Extensions" tab.
Click the "New..." button.
Select the BID file for your extension's class, or enter the class ID.
Continue steps 3-4 for each class in your extension.
Save your MIF file.

 

<!--[if !vml]--><!--[endif]-->


Figure 1: Setting an extension's class IDs in the MIF.

The other thing you need to do is create the public interface for your extension. To do this, you need to create a header file that defines the virtual table (also called simply a vtable) for the classes in your extension and the macros to dispatch against the vtable, along with any other structures or macros required by your extension's classes. Creating the vtable is easy to do, even working in C, thanks to the AEEINTERFACE_DEFINE and AEEGETPVTABLE macros provided by Qualcomm in AEEInterface.h.

The AEEINTERFACE_DEFINE lets you define the vtable for a class. For example, consider an extension to manage a user's to do list, presumably as part of a simple personal information manager (PIM); the interface to the task database might resemble that of the IDatabase interface already provided by BREW. To define the vtable for the task database, named ITaskDatabase, I write:

#define INHERIT_ITaskDatabase( iname ) /
   INHERIT_IQueryInterface( iname ); /
   void          (*Reset)( iname *); /
   ITaskRecord * (*GetRecordByID)( iname *, uint32 ); /
   ITaskRecord * (*GetNextRecord)( iname * ); /
   uint32        (*GetRecordCount)( iname * ); /
   ITaskRecord * (*CreateRecord)( iname * )
AEEINTERFACE_DEFINE( ITaskDatabase );

The first preprocessor instruction defines a new vtable INHERIT_ITaskDatabase for a class descended from the IQueryInterface class. This vtable has five methods, along with the three defined by IQueryInterface itself. In turn, other classes can declare themselves dependent on this vtable using this macro. The second directive, itself a preprocessor macro, creates a new BREW class named ITaskDatabase using the INHERIT_ITaskDatabase macro itself—in essence, defining ITaskDatabase as the interface that inherits from ITaskDatabase, which would a circular definition except for the first macro declaration.

All BREW classes should have a base class of either IBase or IQueryInterface, which itself descends from IBase. As a general rule, if you're developing for later versions of BREW it's almost always better to inherit from IQueryInterface, because doing so gives you a well-defined mechanism for extensibility while ensuring binary compatibility. The IQueryInterface contract requires you to provide three methods:

The AddRef method, which increments the reference count for an instance of an interface.
The Release method, which decrements the reference count for an instance of an interface, and releases the resources used by the interface and the interface itself if the reference count reaches zero.
The QueryInterface method, which returns the requested interface for the indicated interface ID if one is available.
Note: In point of fact, instead of inheriting from IQueryInterface, ITaskDatabase could have inherited from IDatabase, except for two things. First, it's good practice when extending BREW to always inherit from IQueryInterface to ensure compatibility across versions, and second, the IDatabase interface uses BREW's older QINTERFACE macro when defining its interface, which makes it harder to derive child classes.

Of course, no one wants to rummage through a vtable to dispatch against an interface, so QUALCOMM provides the AEEGETPVTBL method that you use to define macros to access each element of your class vtable. For the ITaskDatabase class just defined, I write:

#define ITASKDATABASE_AddRef(p) /
   AEEGETPVTBL((p),ITaskDatabase)->AddRef( p )

#define ITASKDATABASE_Release(p) /
   AEEGETPVTBL((p),ITaskDatabase)->Release( p )

#define ITASKDATABASE_QueryInterface( p, clsid, ppo ) /
   AEEGETPVTBL((p),ITaskDatabase)->QueryInterface((p),(clsid),(pp))

#define ITASKDATABASE_Reset(p) /
   AEEGETPVTBL((p),ITaskDatabase)->Reset( p )

#define ITASKDATABASE_GetRecordByID(p, dwID) /
   AEEGETPVTBL((p),ITaskDatabase)->GetRecordByID((p),(dwID))

#define ITASKDATABASE_GetNextRecord(p) /
   AEEGETPVTBL((p),ITaskDatabase)->GetNextRecord( p )

#define ITASKDATABASE_GetRecordCount(p) /
   AEEGETPVTBL((p),ITaskDatabase)->GetRecordCount( p )

#define ITASKDATABASE_CreateRecord(p) /
   AEEGETPVTBL((p),ITaskDatabase)->CreateRecord( p )

These macros are straightforward, but perhaps the most tedious part of writing the extension's interface, because you must keep the arguments in sync with the arguments of your class methods, and you won't find any errors you make until you actually invoke one of the macros in your code. For large projects, you could probably automate the process of generating the macros from the vtable definition, although I'm not aware of anyone who's gone to the trouble of doing so.

Implementing a BREW Extension

Under the hood, an extension isn't much different from an application—in fact, what separates an application from an extension is that an application has an event handler, registered when you initialize your application. Extensions, as faceless components, don't handle events, but must provide other methods, much as a C++ class does.

Like an application, your extension's entry point is AEEClsCreateInstance, invoked from within the QUALCOMM-provided AEEModGen.c file. A typical AEEClsCreateInstance simply examines the incoming class ID and invokes an extension-specific constructor, like this:

int AEEClsCreateInstance( AEECLSID clsID,
                          IShell * pIShell,
                          IModule *pIModule,
                          void ** ppMod)
{
   switch( clsID )
   {
      case AEECLSID_TASKDATABASE:
         return TaskDatabase_CreateInstance( clsID,
                                             pIShell,
                                             pIModule,
                                             ppMod );
   default:
      return EBADCLASS;
   }
}

It's always a good idea to separate your AEEClsCreateInstance method from your extension's constructor like this, even when you have only a single class in your extension, for three reasons. First, it's much easier to add additional classes later. Second, the AEEClsCreateInstance construct is only applicable when writing dynamically loaded code (code written and installed over the air); if you're writing an extension for OEMs that will be statically compiled into the handset ROM, each of your extension class constructors will be registered in a special table used by BREW to manage in-ROM extensions. Finally, if you decide not to ship your extension as an extension but instead link it directly into your application, you can simply pull the definition of AEEClsCreateInstance and use the constructor function it calls, and the rest of the work you've invested in making your extension, including the public interface header, is still useful.

Your constructor must allocate the memory necessary for the object itself and its vtable, as well as do any other necessary initialization work. As with a well-designed class in C++ or Java, it's safest to keep your constructor as simple as possible, doing as little setup work as is necessary. Whereas the QUALCOMM BREW interfaces don't have a lot of examples for two-phase object construction (in which you first create an object and then initialize it), it's a good bet that under the hood most complex classes like IMedia probably do something close to this, because two-phase object construction is a well-understood design pattern that provides good error handling facilities. A typical extension constructor looks like this:

typedef struct _STaskDatabaseData
{
   /// The vtable (must be first)
   AEEVTBL(ITaskDatbase) *pvt;

   IModule *pIModule;
   IShell *pIShell;
   uint32 nRefs;

   // Our member variables
   ...
} STaskDatabaseData;

int ITaskDatabase_CreateInstance( AEECLSID clsID,
                                  IShell *pIShell, IModule *pIModule,
                                  void **ppMod)
{
   STaskDatabaseData *pThis = NULL;
   AEEVTBL(ITaskDatbase) *modFuncs;
   int16 nSize = sizeof( STaskDatabaseData ) +
                 sizeof( AEEVTBL(ITaskDatbase) ) ;

   if( !ppMod || !pIShell || clsID != AEECLSID_TASKDATABASE )
      return EFAILED;

   pThis = (STaskDatabaseData *) MALLOC( nSize );
   *ppMod = pThis;

   if ( NULL == pThis ) return ENOMEMORY;
   ZEROAT( pThis );

   modFuncs = (AEEVTBL(ITaskDatbase) *)((byte *)pThis +
                                        sizeof( STaskDatabaseData ));
   modFuncs->AddRef          = ITaskDatabase_AddRef;
   modFuncs->Release         = ITaskDatabase_Release;
   modFuncs->QueryInterface  = ITaskDatabase_QueryInterface;
   modFuncs->Reset           = ITaskDatabase_Reset;
   modFuncs->GetRecordByID   = ITaskDatabase_GetRecordByID;
   modFuncs->GetNextRecord   = ITaskDatabase_GetNextRecord;
   modFuncs->GetRecordCount  = ITaskDatabase_GetRecordCount;
   modFuncs->CreateRecord    = ITaskDatabase_CreateRecord;
   INIT_VTBL(pThis, ITaskDatabase, *modFuncs);

   // initialize the data members
   pThis->nRefs      = 1;
   pThis->pIShell    = pIShell;
   pThis->pIModule   = pIModule;

   // Add References
   ISHELL_AddRef( pThis->pIShell );
   if ( pThis->pIModule ) IMODULE_AddRef( pThis->pIModule );

   return AEE_SUCCESS;
}

 

The work done by the constructor leads to a lot of code, but as you see from the listing, other than a memory allocation it's really all bookkeeping. It begins by computing the size of the required result structure, which must be large enough to hold both private data for the object as well as its virtual table. Next, I allocate the necessary space, returning if the allocation fails or else zeroing the resulting memory (which isn't strictly necessary if your constructor is just going to fill out all of its member variables at this point). With the memory in hand, I find the location of the vtable, which sits after the static data (see Figure 2) in the newly created object, and then assign each of the method pointers in the vtable to the appropriate function. Finally, I initialize each of the data members and return the newly created object.

<!--[if !vml]--><!--[endif]-->

Figure 2: Identifying the vtable

Of course, every constructor deserves a well-balanced destructor, which falls into the Release method for the class:

static uint32 ITaskDatabase_Release(ITaskDatabase *p) {   STaskDatabaseData *pThis = (STaskDatabaseData *)p;   if (!pThis ) return 0;   pThis->nRefs--;   if (pThis->nRefs != 0) return pThis->nRefs;   RELEASEIF( pThis->pIShell );    RELEASEIF( pThis->pIModule );   FREE_VTBL(pThis, ITaskDatabase);   FREEIF( pThis );   return 0; }This function—which need have only file scope, because it's always accessed though the vtable anyway—simply decrements the reference count and either returns the new reference count, or frees the members of the object before releasing the object's vtable and the object itself. You should note the casting skullduggery that occurs between the single argument to release—the interface pointer—and the underlying data structure that contains the data associated with the interface instance. It's a bit of a hassle, and many times in looking at extensions written by others you may see a macro like this:

#define   THIS_FROM_INTERFACE(p, objtype) objtype *pThis = p;I explicitly choose not to do that because I've found by coaching others that hiding the cast behind a macro only makes it harder to understand how an extension works for newcomers.

The remainder of your methods will look much like ITaskDatabase_Release: First convert the interface pointer into a pointer to the underlying representation, and then do whatever your method is supposed to do.

Summary
The BREW extension concept gives you a powerful mechanism with which to partition your code into components that can be shared between multiple applications. In addition to providing access to the same interface used by BREW components (making it easy for developers to learn to use your extensions), the platform provides the ability to offer extensions seamlessly through the BDS to both your own applications and those of other companies, letting you strike the perfect balance between internally sharing code and making your technologies available to other application developers in a profitable way.

For More Information
QUALCOMM BREW: http://www.qualcomm.com

QUALCOMM BREW Extensions: http://brew.qualcomm.com/brew/en/developer/resources/ad/extensions.html

About the Author
Ray Rischpater is the chief architect at Rocket Mobile, Inc., specializing in the design and development of messaging and information access applications for today's wireless devices. Ray Rischpater is the author of several books on software, including Development Including eBay Application Development and Software Development for the QUALCOMM BREW Platform, both available from Apress, and is an active Amateur Radio operator. Contact Ray at kf6gpe@lothlorien.com.

 

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
应用背景为变电站电力巡检,基于YOLO v4算法模型对常见电力巡检目标进行检测,并充分利用Ascend310提供的DVPP等硬件支持能力来完成流媒体的传输、处理等任务,并对系统性能做出一定的优化。.zip深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值