FAQ设置 »» BREW

内容列表


如何能够将已编译的应用程序传输到电话上?
使用 BREW AppLoader 将应用程序上载到电话。以下示例显示了 名为 "myapp" 的应用程序的文件和位置。

/brew/sch/myapp.mif - 由 BREW MIF 编辑器生成
/brew/sch/myapp/myapp.bar - 由 BREW 资源编辑器生成
/brew/sch/myapp/myapp.mod - 使用 ARM BREW 编译器编译和链接
/brew/sch/myapp/myapp.sig - 数字签名

电话上的所有目录和文件名都必须采用小写形式。特定于您的应用程序的附加文件也可能要复制到应用程序目录, 或应用程序目录的子目录。

数据签名是使用 BREW TestSig Generator 生成的,随后会用应用程序名称重命名。.sig 文件名的第一部分必须与 .mod 文件名的第一部分相同。

上载文件后,请重置电话。

有关详细信息,请参阅构建可下载的 BREW 应用程序的方法。
如何编译在手持设备上运行的应用程序?
要 编译电话使用的应用程序,需要使用工具的 ARM 开发者套件,这些工具用于编译和链接 已使用基于 PC 的 BREW SDK 及相关工具开发的项目。运行使用 ARM 工具编译的应用程序需要一部 启用 BREW 的 电话、一根用于建立至电话的串行连接的电缆,和文件复制软件 (如 QUALCOMM 的 BREW AppLoader 应用程序)。
如何将 Device Configurator 文件 (.qsc) 上载到电话?
Device Configurator 文件 (.qsc) 由 BREW Emulator 应用程序使用, 无需在电话上运行该应用程序。
如何调试模拟器和电话上的输出?
应用程序在 BREW Emulator 上运行时,DBGPRINTF 向 Visual C++ 调试窗口输出消息。目前,电话上运行应用程序时,DBGPRINTF 消息不可用。 正在开发对通过串行连接向主机 PC 输出调试消息的支持。
可以使用哪些编译器编译 BREW 应用程序?
ARM BREW 编译器可以用于编译 BREW 应用程序。
在 Thumb 模式下编译应用程序时应遵守哪些指导原则?

1.AEEMod_Load() 函数必须移至一个不同的源文件中,而且此文件必须在 ARM 模式下编译。AEEMod_Load() 必须始终在 ARM 模式下编译。
2.在 Thumb 模式下编译的所有文件必须启用 INTERWORK 编译器选项。如果此选项在生成文件中未启用,则该应用程序很可能崩溃。
将 ARM BREW 编译器或 ARM 开发套件安装到有空格的路径中时该怎么办?
解决办法是按照以下方式修改您的生成文件:
1.标出 ARMBIN、ARMINC 和 ARMLIB 声明。
2.将完整路径添加到 ARMCC、LD 和 HEXTOOL 变量。确保使用双引号括起路径。
在哪里可以找到安装 ARM BREW 编译器或 ARM 开发套件的提示?
有关详细信息,请参阅 ARM 编译器安装提示和 构建可下载的 BREW 应用程序的方法。
ARM BREW 编译器的最新版本是什么以及 在哪里可以找到维护补丁(如果有)?
最新版本是 ARM BREW 编译器 1.0。ARM BREW 编译器 1.0 是以 ARM 开发套件 (ADS) 1.1 为基础的。发布 ARM BREW 编译器 1.0 后,armlink(725 版)的维护补丁亦也发布。该补丁可以从 http://www.arm.com/support.nsf/html/downloads-ads11 下载。

ARM BREW 编译器版本 1.2 预计在 2002 年 7 月份发布。该版本支持 Windows XP,已购买了 ARM BREW 编译器 1.0 的开发者可以免费升级到该版本。
BREW 有哪些 Java 开发选项?
尽 管 QUALCOMM 本身不为 BREW 提供 Java 运行时间,但是我们正在与几个合作伙伴一起为我们的开发者提供选择。目前,我们无法提供对这些 Java 解决方案的访问,但是有望在近期提供。同时,如果您正在开发 J2ME/CLDC/MIDP 应用程序或 midlet,则您可以使用任何标准的 Java 开发环境。同时有望在某些执行环境下直接使用 BREW API。
使用 C++ 时应该注意什么?
肯定应该使用 IHEAP 内存管理例程。最好过载每个类中的新操作符和删除操作符,在这些过载操作符实施中分别使用 AEEStdlib 函数 MALLOC 和 FREE。

下面是一个示例:


void *operator new ( size_t size) {
return MALLOC (size) ;
}
// 重载删除操作符
void operator delete(void * ptr) {
FREE(ptr) ;
}
您无法使用将从现有 BREW 类中派生的 C++ 虚拟函数或继承机制。例如,您无法定义 CApplet 类并让它从 IApplet 接口中派生。我们正在克服这一局限。

在 C++ 中您只能将您的小程序的数据定义为类。例如,您不定义一个包含所有应用程序特定数据的结构 CmyApp,而是可以将其变成类。您可以继续将其大小传递到 AEEApplet_New。在 HandleEvent(及其它)函数中将向您传递回您的 C++ 对象的指针,然后您可以按 C++ 的方式使用它。
使用汇编程序时应该注意什么?
您应该注意分支指令,即从您的代码内到模块外的长分支。您的代码分支外的任何内容都应该使用寄存器。而且,照常您应该没有全局和静态数据。
BREW SDK 的操作系统要求是什么?
由于 Unicode 支持的要求,SDK 仅在 Microsoft Windows NT™ 4.0、Windows 2000™ 和 Windows XP™ 平台上运行。BREW SDK 不支持 Windows 98。
BREW 是否支持多线程?
目前,BREW 不支持多线程。BREW 的确支持合作性的多任务处理。
我是否可以从 SDK 链接到 Windows DLL?
不可以。您应该将 SDK 当作完全集成的独立平台,包括不使用 C 标准库。BREW 提供了最常见函数的端口。
BREW 是否在非 Qualcomm 芯片集上可用?
目前,仅 QUALCOMM MSM 芯片集支持 BREW,但是我们预计将来非 Qualcomm 芯片集也可以支持。目前还没有公布非 QUALCOMM MSM 系列芯片集的芯片集支持 BREW 的时限。
哪些设备可以支持 BREW?
目 前,仅基于以下 QUALCOMM 芯片集的设备可以支持 BREW。MSM3100、MSM3300、MSM5000、MSM5100 和 MSM5105。另外,要使某电话或其它设备支持 BREW,它必须是“启用 BREW 的”且具有设备生产商提供的 BREW 运行时版本。
小程序必须处理哪些事件?
除明显的 EVT_APP_START 和 EVT_APP_STOP 外,您的小程序必须支持 EVT_APP_SUSPEND 和 EVT_APP_RESUME 才能通过 True BREW 测试。
使用电话的“End(结束)”键关闭小程序和使用“Clear(清除)”键关闭小程序有何区别?
特定设备(电话)的 OEM 为以下两个活动指定按键:
按下将关闭当前应用程序的按键。大多数 OEM 将此键指定为 AVK_CLR 键。
按下将关闭所有应用程序的按键。大多数 OEM 将此键指定为 AVK_END 键。
按 下 AVK_END 时,BREW 会立即将 EVT_APP_STOP 发送到激活的小程序,而不先发送 AVK_END 键。另外,将在卸载小程序前调用向 AEEApplet_New() 提供的 FreeAppData() 回调例程;将不发生任何其它事件或回调。

按 下 AVK_CLR 时,BREW 首先将此事件发送到小程序。如果小程序不处理该事件(即在 HandleEvent 中返回 FALSE),只有在此时 BREW 才会关闭应用程序。在您的 AVK_CLR 处理程序的实施过程中,记住同时要调用 FreeAppData,如下所示:
case AVK_CLR:
if (pMe->OnMainMenu == TRUE) {
// 小程序位于主菜单上, 因此按下 CLR 键将退出小程序
HelloWorld_FreeAppData(pi); //清除
return FALSE; //返回 FALSE 以使 BREW 立即关闭应用程序
}
else { // 不在主菜单上。
// 因此,按下 CLR 键将使小程序取消一级菜单操作
// 嵌套。 显示菜单结构中的前一级菜单
return TRUE;
}


确保您的 FreeAppletData() 会正确清除分配的所有内存和资源。由 CreateInstance、CreateDialog、MALLOC 等创建的所有对象和接口必须对 Release 或 FREE 有关联调用。

如何处理电池电量不足警告?
在电池电量不足警告情况下,BREW 会向正在运行的应用程序发送 EVT_APP_SUSPEND 事件。为了处理电池电量不足的情况,必须正确处理 EVT_APP_SUSPEND 和 EVT_APP_RESUME 事件。
ISHELL_SendEvent() 和 ISHELL_ PostEvent() 有什么区别?
ISHELL_SendEvent() 和 ISHELL_PostEvent() 函数都用来向指定类发送事件。两者的区别在于发送事件的方式。

对 于 ISHELL_SendEvent(),事件会立即发送到指定 Class ID 的应用程序。如果当前没有该类的实例,AEE Shell 将创建一个实例,然后使用指定事件代码和数据参数调用 IAPPLET_HandleEvent() 函数。除非应用程序愿意自动启动,否则事件处理完后它即会终止。

除目标类的 IAPPLET_HandleEvent() 函数不立即调用外,ISHELL_PostEvent() 类似。事件进入队列,稍后发送。这样,调用应用程序可以不中断地继续进行处理。

ISHELL_SendEvent() 函数允许控制目的小程序,并直接向目的小程序发送事件。对使用 ISHELL_PostEvent() 传递的事件的处理会延迟到下一个事件循环。在处理其它事件的同时允许程序不间断执行方面,此函数非常有用。

我的应用程序可以注册哪些通知事件?
应用程序可以注册以下系统通知:


TAPI (Class ID: 0x01001007)
NMASK_TAPI_STATUS 0x0001 TAPI 状态改变事件
NMASK_TAPI_SMS_TEXT 0x0002 进入的 SMS
NMASK_TAPI_SMS_TS 0x0004 特定电信业务 ID 上的 SMS 消息

INETMGR
NMASK_OPENED 0x0001 网络层可用
NMASK_CLOSED 0x0002 网络层已关闭
NMASK_UDP_LISTEN 0x8000 UDP 监听
NMASK_IDLE 0x0004 网络层可用且空闲
(包含在 BREW SDK 1.1 及更高版本)

应用程序也可以注册从其它动态应用程序和模块接收通知。有关详细信息,请参阅 SDK 随附的 INotifier 用法示例。
如何从“Undef Inst 例外”恢复电话?
“Undef Inst 例外”即“未定义指令例外”,意指程序指针跳转到包含未定义指令的代码段。这可能是由于内存错误、堆栈超限或电话上小程序代码和 BREW 图像间与版本相关的不兼容引起的。

内存在电话上比在模拟器上运行时更紧张。因此,内存和堆栈超限问题更可能在电话上出现。应该进一步检查您的代码与内存相关的部分,特别是自动变量数组和已分配内存的最大大小总量。

要清除电话,请将电池取出几秒钟。
什么是“可重入数据中止”?
“可重入数据中止”例外通常是由堆栈超限引起的。小程序在电话上运行时,堆栈较小,因此在模拟器上运行时更可能出现堆栈超限问题。此问题的解决办法是减小堆栈上对象的大小或减少这些对象的数量并使用在堆上分配的对象,而不是自动变量。

据报道,电话上的小程序代码和 BREW 图像之间与版本相关的不兼容也可能导致此类例外。
什么是“Pref 中止例外”?
“Pref 中止例外”通常表示对电话操作系统重要的内存被破坏。此例外的两个最常见原因是数据破坏(即从数组末尾逃逸)和堆栈超限。
什么是“SWI 例外”?
“SWI 例外”通常与堆内存管理不善有关。堆内存错误的最常见原因如下:覆盖所分配数组的界限、多次释放对象以及写入难以操纵的指针或 NULL 指针。

除了更常见的原因外,堆栈超限也可能导致 SWI 例外。建议将堆上而非堆栈上的大缓冲区作为自动变量分配。
为什么模拟器在我的应用程序退出时会显示有我的应用程序名的空白屏幕?
这 种情况是由于退出应用程序前未释放分配的全部内存所引起的。必须使用 FREE 或 IHEAP_Free() 释放您用 MALLOC 或 IHEAP_Malloc() 分配的任何缓冲区。对于您创建的 BREW 类的任何实例,必须调用相应的释放函数来释放该实例。

BREW 中的内存结构如何工作?
BREW 设备拥有闪存 RAM 和堆 RAM。您可以将闪存 RAM 当作硬盘驱动器。程序在这里存储,但不从此处运行。您可以将堆 RAM 当作内存堆。假定您有一个应用程序,大小为 35k。这样就需要 35k 闪存 RAM 用于在设备上存储。运行应用程序时,这 35k 将被全部加载到堆 RAM,而且还需要分配更多的堆 RAM。释放小程序时,会释放此堆 RAM。它将仍然占用闪存 RAM 上的空间,直到从设备上删除。
小程序的大小限制是什么?
BREW 小程序的大小受可用空闲文件系统空间量和可用 RAM 量的限制。

BREW 小程序在执行时会加载到 RAM 中;任何剩余的 RAM 可用于内存分配、加载资源、创建控件等。可用 RAM 的多少取决于电话的类型及其配置。例如,在 QCP-3035 上,可用 RAM 是 90K。30K 属于 BREW 和其它电话相关的任务,60K 可由小程序使用。运行 BREW 的每个电话上的可用内存可以从此处获得。

小程序大小的另一个限制因素是堆存储碎片。如果最大可用内存块 小于小程序的大小,则不会加载小程序。使用 IHEAP_CheckAvail() 确定是否可以分配足够大的内存块。IHEAP_GetMemStats() 可用来确定已使用的 RAM 量。如果您的一个小程序对于某设备而言太大,解决办法是将该应用程序分割成多个小程序。有关如何能够避免产生堆存储碎片的详细信息,请参阅内存分配。

注意:
为了在 BREW Emulator 上测试内存不足的情况,请将设备配置文件中的堆大小减小相应的量。
BREW System Info(系统信息)屏幕报告的是可用闪存量,而不是可用 RAM 量。
为什么小程序无法直接从闪存 RAM 运行?
BREW 将应用程序加载到堆 RAM 中,因为 EFS 上的文件是作为非连续块存储的。
BREW-SDK 中 MALLOC() 和 IHEAP_Malloc() 函数的区别是什么?
MALLOC 和 IHEAP_Malloc() 完全一样,如同 FREE 和 IHEAP_Free() 也是完全一样的。在 BREW SDK 的早期版本中,malloc 是 IHEAP 接口的一部分,后来为便于使用而合并到助手函数中。两者仍可以向后兼容,但建议使用 MALLOC 和 FREE,而不是 IHEAP_Malloc() 和 IHEAP_Free()。

小程序若希望获得当前内存使用统计数字 (IHEAP_GetMemStats()) 或检查是否可以分配一定大小的内存块 (IHEAP_CheckAvail()),只需要创建 IHeap 接口。
如何在模拟器上测试应用程序以确定它在内存不足情况下是否正确运行?
这可以通过将设备配置文件(.qsc 文件)中的堆大小减小到与 .mod 文件的大小差不多来实现。如果应用程序的任何运行时分配失败,您可以测试您的应用程序是否可以正确处理这些故障。

由于 mod 文件的大小大大小于在模拟器中运行的 dll,模拟器估计 mod 文件的大小是 dll 的 10%。

要正确运行此测试,请单独创建一个仅包含测试中应用程序的 MIF 的目录,并将模拟器指向这个新目录(File(文件)->Change MIF Dir(更改 MIF 目录))。
如何从 BREW 应用程序发送 SMS 消息?
目前,BREW 无法从 BREW 小程序内发送 SMS 消息。 BREW 应用程序只能接收 SMS 消息。
SMS 消息是否可以发送给不活动的小程序?
是, 可以将 SMS 通知事件发送到一个应用程序,即使它目前不活动。 为此,BREW 首先加载小程序,然后再向它发送 EVT_APP_MESSAGE 事件。 应用程序可以通过调用 ISHELL_StartApplet() 自动启动,也可以不调用 ISHELL_StartApplet() 而“无声地”处理该事件。
何在 BREW 应用程序中处理进入的 SMS 消息?
有关 BREW 中 SMS 的详细信息,请参阅在 BREW 中处理 SMS。
进入的 SMS 消息是否可以用 SDK 模拟?
SMS 模拟将在 BREW SDK 版本 1.1 中实现。
在 Sharp Z-800 上收到非 BREW SMS 消息时让正在运行的 BREW 应用程序中止需要进行哪些设置?
在 Sharp Z-800 手持设备上,您必须进行以下设置才能接收非 BREW SMS:

Main Menu(主菜单)-> Setup/Tool(设置/工具)-> BREW Setting(BREW 设置)-> OFF(关闭)
使用 ITAPI_MakeVoiceCall 进行语音呼叫时,对 Privacy Alert(隐私警告)响应 No(否)会使应用程序挂起。有什么解决办法?
这是 BREW SDK 版本 1.0.1 中的缺陷,将在以后版本中修复。

对 于 BREW SDK 版本 1.0.1,建议解决办法是用户按两次 No(否)(即按 Select(选择)键两次)。 显示 Privacy Alert Yes/No(隐私警告是/否)时,所有的按键事件直到 第一次按下 Select(选择)键后才会转至该对话框。在第一次按下 Select(选择)键(对话框)后,后续按键事件将转至应用程序。

步骤概述如下:

1. 在小程序结构中添加一个布尔型 "madeVoiceCall"
2. 如果 ITAPI_MakeVoiceCall() 返回 Success,则将 madeVoiceCall 设置为 TRUE
retValue = ITAPI_MakeVoiceCall(pMe->p_tapi, PHONE_NUM,
AEECLSID_SAMPLEAPP);
ITAPI_Release(pMe->p_tapi);
if(retValue == SUCCESS) {
pMe->madeTapiCall = TRUE;
}


3. 处理应用程序处理程序函数中的 EVT_KEY。如果 Select(选择)键已按下且 madeVoiceCall 为 TRUE,则用户已选择 No(否)作为对 Privacy Alert(隐私警告)的响应。设置 madeVoiceCall = FALSE 并 刷新屏幕。
case EVT_KEY:
if (wParam == AVK_SELECT && pMe->madeTapiCall ==
TRUE) {
// 刷新屏幕
pMe->madeTapiCall = FALSE;
}
return TRUE;


4. 如果用户对 Privacy Alert(隐私警告)选择“Yes(是)”,则应该已中止了该应用程序。 恢复时,必须清除 madeVoiceCall 标记。
case EVT_APP_RESUME:
if(pMe->madeTapiCall == TRUE) {
pMe->madeTapiCall = FALSE;
}
... ... ...
... ... ...
return TRUE;


使用 ITAPI_MakeVoiceCall 进行语音呼叫后,为什么在我对“Return to Application(返回应用程序)”提示响应“No(否)”时我的应用程序似乎要重新启动?
请 确保参数 clsReturn(调用结束后要启动的应用程序) 在 ITAPI_MakeVoiceCall 的调用中为 0(零)。如果您将 clsReturn 指定为您的应用程序的 Class ID, 则 BREW 将尝试创建另一个应用程序的实例,而不是 恢复您的应用程序。
终止数据呼叫后立即进行语音呼叫时应遵守哪些指导原则?
释 放最后一个套接字之后,终止 PPP 连接之前,BREW 要等待网络延迟时间(默认为 30 秒钟)过期。实际断开 PPP 连接需用大约 3 秒钟时间。因此释放最后一个套接字和调用 ITAPI_MakeVoiceCall() 之间必须经过(延迟时间 + 3 秒钟)的时间。在释放最后一个套接字和进行语音呼叫之间应该使用一个(延迟时间 + 3 秒钟)的计时器。

例如:
ReleaseNetAndSocket(pMe);
//以下 LINGER_TIME 的单位为秒
ISHELL_SetTimer(pMe->a.m_pIShell, (LINGER_TIME +3) * 1000,
Timer_CB, (void *)pMe);

在计时器回调代码中,可以使用 ITAPI_MakeVoiceCall() 进行语音呼叫。

有关 ITAPI_MakeVoiceCall() 的详细信息,请参阅下面的 FAQ:

使用 ITAPI_MakeVoiceCall 进行一个语音呼叫后,为什么在我对 “Return to Application(返回应用程序)”提示响应“No(否)”时,我的应用程序似乎被重新启动? FAQ。
使用 ITAPI_MakeVoiceCall 进行一个语音呼叫时,对 Privacy Alert(隐私警告)响应 No(否)会使应用程序挂起。有什么解决办法? FAQ。

注意:在 BREW SDK 版本 2.0 中,此 3 秒延迟已内置到 BREW 中。 应用程序无需在它的计时器中实现另外的三秒钟。

在数据呼叫之后立即进行语音呼叫时, 为什么我看到的是 Privacy Alert(隐私警告)而不是“Return to Application(返回应用程序)”?
请参阅“终止数据呼叫之后立即进行语音呼叫时应遵守哪些 指导原则?”FAQ。
如何获取正在运行我的应用程序的 设备电话号码?
应用程序可以调用 ITAPI_GetStatus() 来获取程序所运行 设备的电话号码。电话号码将位于 TAPIStatus.szMobileID 中。

例如:
TAPIStatus tapiStat;
if (ITAPI_GetStatus(pMe->p_tapi, &tapiStat) == EBADPARM) {
DisplayOutput((IApplet*)pMe, 6, "TAPI Status fetch failed");
}
DisplayOutput((IApplet*)pMe, 4, "Mobile ID:");
DisplayOutput((IApplet*)pMe, 5, tapiStat.szMobileID);

BREW 何时会提供 HTTP 支持?
HTTP 支持在 BREW SDK 版本 1.1 中通过 AEEWeb 接口提供。对于 SDK 的早期版本,请使用 ISocket 接口连接到服务器的 HTTP 端口并发送 HTTP“get”和“post”请求。
GetHostByName() 为什么在我的电话上不起作用?
要使 INETMGR_GetHostByName() 正确工作,电话的域名服务器 (DNS) 设置必须正确配置。这通常由服务提供商负责。在这种情况下,应将电话退回销售点或授权维修中心,以便正确配置其 DNS 设置。

目前,北美客户可以将电话寄至 QUALCOMM 进行 DNS 配置。这是临时性的解决办法,等到服务提供商安排好后,将由服务提供商处理客户电话的 DNS 配置事宜。有关将电话寄至何处进行 DNS 配置的信息,请参阅本 FAQ。
使用 GetHostByName() 时为什么在 dnsresult.addrs 中获得 0.0.0.0 值?
这可能是由于 GetHostByName() 回调函数中错误处理的不正确实施引起的。NetSocket 示例的 GetHostByName() 回调函数中的错误处理实施不正确。请使用以下示例实施:
if(pMe->dnsresult.nResult > 0 && pMe->dnsresult.nResult
<= AEEDNSMAXADDRS) {
// DNS 查找 成功
// 处理 dnsresult.addrs
for(i = 0; i < pMe->dnsresult.nResult; i++) {
SPRINTF(szMsg,"Addr=:%x",pMe->dnsresult.addrs[i]);
DisplayOutput(pMe,i+4,szMsg);
}
} else {
// DNS 失败 - 错误代码为 dnsresult.nResult
SPRINTF(szMsg, "DNS:error %d", pMe->dnsresult.nResult);
DisplayOutput(pMe, 2, szMsg);
}


由于 BREW 中没有 TCP/IP 刷新命令,如何能够确认命令立即发出?
无法确定何时发出 TCP/IP 命令;BREW 不提供此功能。
BREW 支持的最大数据包大小是什么?
最大数据包大小由 OEM 确定,对于各生产商的电话各不相同。BREW 不对此限制进行控制。
传输大文件时,是否必须在发送前将文件分割成数据包,或者 BREW 是否会代我执行此操作?
您只应发送能够发送的内容,ISOCKET_Write() 将告诉您实际上已经发送了多少数据。如果有数据尚未发送,只需用您对 ISOCKET_Write() 的上次调用显示的量来抵消您的数据指针即可。

例如:
void CommonWriteCB (void *pUser) {
char *psz = NULL;
int cbWrite;
... ... ...
SampleApp * pMe = (SampleApp*)pUser;
// pszData 是要写入的数据
psz = pszData;
cbWrite = STRLEN (pszData);

// 循环直至已写入所有字节。
while (cbWrite > 0) {
rv = ISOCKET_Write(pMe->m_pISocket, (byte *)psz,
(uint16)cbWrite);

if (rv == AEE_NET_WOULDBLOCK) {
// 无法成功写入字节。注册
// 回调,稍后再试。
ISOCKET_Writeable(pMe->m_pISocket,
CommonWriteCB, (void *)pMe);
return;
} else if (rv == AEE_NET_ERROR) {
// 写入错误。
ReleaseNetAndSocket(pMe);
return;
}

// 如果只写入了一部分,则循环并写入剩余部分
cbWrite -= rv;
psz += rv;
}
... ... ...
}

同时可以打开多少个套接字?
此限制因 OEM 而异,将依据运行您的小程序的设备而不同 - 它不由 BREW 设置。
是否在主线程环境下调用网络回调?如果是这样,在当前环境下如何保持数据完整性?
在调用程序将控制权返回 AEE 事件循环之后的某个时间点,所有网络回调发生在同一线程上下文中。如果您的应用程序正在忙于执行某项操作,则会将回调排队,然后一旦您的应用程序将控制权返回 AEE 事件循环将进行调用,从而确保数据完整性。

从套接字读取时,电话读取一次能够读取的所有内容,而模拟器一点一点读取大数据包。为什么?
这是电话的局限性。

您 的程序应该调用 ISOCKET_Readable(),以便在可以从流中读取更多数据时得到通知。每当数据可用时将调用 ISOCKET_Readable() 注册的回调例程 - 通常可以在此时调用 ISOCKET_Read()。只要您的程序期望获得更多数据,就继续调用 ISOCKET_Readable()。

rv = ISOCKET_Read(piSock, (byte *)szBuf, sizeof(szBuf));
if (rv == AEE_NET_WOULDBLOCK) {
// WOULDBLOCK => 此时无其它可用数据
// 注册回调,稍后读取数据。
ISOCKET_Readable(piSock, CommonReadCB, (void*)pMe);
return;
}
else if (rv == AEE_NET_ERROR) {
// 从套接字读取时出错
ReleaseNetAndSocket (pMe);
}
else if (rv > 0) {
// 数据的 rv 字节已从套接字读入
// szBuf

// 读取剩余数据
ISOCKET_Readable(piSock, CommonReadCB, (void*)pMe);
}
else { // rv == 0
// 没有其它数据要接收。
// 对方已断开连接。
ReleaseNetAndSocket (pMe);
}


如何检查我的套接字和 PPP 连接的状态?
您可以使用 INETMGR_NetStatus() 检查 PPP 连接的状态(激活、未激活、正在打开或正在关闭)。
NetState netState = NET_INVALID_STATE;
AEENetStats netStats;
netState = INETMGR_NetStatus (pINetMgr, &netStats);


您可以使用 INETMGR_OnEvent() 监控 PPP 连接和套接字连接的状态。请参阅 AEENet.h 中的 NetMgrEvent 枚举以获取包括所有网络和套接字事件的列表。
// 注册接收网络和套接字事件
INETMGR_OnEvent(piNet, (PFNNETMGREVENT)CheckNetStatus,
(void*)pMe, TRUE);

//接收到网络/套接字事件时调用回调
static void CheckNetStatus(void* cxt,
NetMgrEvent evt, uint32 dwData) {
SampleApp *pMe = (SampleApp*)cxt;

if(evt == NE_PPP) {
// 网络事件。 dwData = NetState 枚举值
// 请参阅《BREW API 参考资料》文档中的 NetState 数据结构
}
if(evt == NE_SO_CLOSING || evt == NE_SO_CLOSED) {
// 套接字事件 - 正在关闭或已关闭。
// DwData 是指向套接字的指针
}
... ...
return;
}


是否可以在两个电话间传输数据?
我们发现电话间的对等连接是不可靠的,当两个电话在同一子网上时会失败。因此,最好使用代理服务器,使用服务器作为媒介在电话间传输数据。
启用 BREW 的设备是否可以用作服务器?
除了明显的内存和性能限制外,在电话上运行 BREW 应用程序时无法监听套接字连接。这些因素使得在 BREW 上实施服务器十分困难。

是否有方法可以确定套接字已经连接?
有两种方法可以确定套接字是否已经连接。最容易的方法是检查 ISOCKET_Connect() 的返回值。套接字已经连接时,ISOCKET_Connect() 将返回 EISCONN。

同样,也可以通过使用 INETMGR_OnEvent() 注册套接字状态改变事件 NE_SO_CLOSING 和 NE_SO_CLOSED 来监控套接字连接状态。通过使用此方法,您的应用程序可以避免试图连接已经连接的套接字。

要了解有关网络和套接字事件(包括 NE_SO_CLOSING 和 NE_SO_CLOSED)的详细信息,请参阅以下包含文件:AEENet.h。

例如:
// 注册接收网络和套接字事件
INETMGR_OnEvent(piNet, (PFNNETMGREVENT)CheckNetStatus,
(void*)pMe, TRUE);

//接收到网络/套接字事件时调用回调
static void CheckNetStatus(void* cxt, NetMgrEvent evt,
uint32 dwData)
{
SampleApp *pMe = (SampleApp*)cxt;

if(evt == NE_SO_CLOSING || evt == NE_SO_CLOSED) {
// 标记设置为 false,说明套接字已断开
pMe->m_SocketConnected = FALSE;
ReleaseNetAndSocket(pMe);
}
if(evt == NE_SO_CONNECTED) {
// 标记设置为 true,指示套接字已连接
pMe->m_SocketConnected = TRUE;
}
return;
}


为什么 ISOCKET_Release() 在应该返回 0 值时却返回 1?
应用程序调用 ISOCKET_Release() 时,ISocket 对象的内部状态更改为“正在关闭”,BREW 开始等待异步“已关闭”事件。由于回调中收到已关闭事件,ISocket 对象的引用计数递增,防止它在内部状态改变为“已关闭”前释放。

7o.问:我试图创建 Net Manager 的实例 (AEECLSID_Net) 时为什么 ISHELL_CreateInstance 返回 ECLASSNOTSUPPORT?
答:这可能是因为您的小程序对 MIF 文件的权限而引起的。请在 MIF 编辑器中打开 MIF 文件,检查复选框中的网络权限,然后保存。
我是否可以获得监听 TCP 套接字?
不可以。我们建议使用 UDP,具体地说是 ISOCKET_Bind 和 ISOCKET_RecvFrom。
BREW 是否支持编块套接字?
不支持。BREW 使用异步套接字。您可以使用 ISOCKET_Readable 或 ISOCKET_Writeable 以便在安全读写时得到通知。
不在蜂窝电话覆盖范围时如何处理?
电话不在覆盖范围时,数据呼叫会中断。发生这种情况时,BREW 会清除正在使用的基本 OEM 套接字。应用程序使用的 ISocket 继续存在,应用程序将在试图使用它们时获得相应的错误响应。

对于同步 (TCP/IP) 数据通信,有两种方法可以处理可能错误的套接字:

1. 检查从所有套接字操作中返回的值

从套接字中读取或写入会导致返回 AEE_NET_ERROR。在您的代码中,您应该检查套接字连接、读写的返回值并采取相应措施。

在下面的代码片段中,相应操作是显示主菜单:
// 连接回调
static void SampleApp_ConnectCB(void *cxt, int err) {
SampleApp *pMe = (SampleAppApp*)cxt;

if (err) {
// 连接失败
SampleApp_CleanUp(pMe);
//清除 net manager 和
// 套接字
ShowMainMenu(pMe);
return;
}
... ... ... ...
}

// 正在写入
iRet = ISOCKET_Write(pMe->m_piSock, (byte*)Request,
(uint16)STRLEN(Request));
if (iRet == AEE_NET_ERROR) {
// 写入错误
SampleApp_CleanUp(pMe);
//清除 net Manager 和套接字
ShowMainMenu(pMe);
return;
}

// 正在读取
iRet = ISOCKET_Read(pMe->m_piSock, (byte*)buf,
sizeof(buf));
if (iRet == AEE_NET_ERROR) {
// 读取错误
SampleApp_CleanUp(pMe);
//清除 net manager 和套接字
ShowMainMenu(pMe);
return;
}


2. 注册网络/套接字状态中的更改

处理数据呼叫正在进行时电话不在覆盖范围情况的另一种方法是使用 INETMGR_OnEvent() 注册网络和套接字事件,并在接收到网络/套接字状态更改通知时采取相应措施。在下例中,设置了一个标记,表明套接字错误。使用前要检查套接字状态。

有关网络和套接字事件的详细信息,请参阅 aeenet.h 包含文件中的 NetMgrEvent 和 NetState 枚举。

例如:
// 注册接收网络和套接字事件
INETMGR_OnEvent(piNet, (PFNNETMGREVENT)CheckNetStatus,
(void*)pMe, TRUE);

//接收到网络/套接字事件时调用回调
static void CheckNetStatus(void* cxt, NetMgrEvent evt,
uint32, dwData) {
SampleApp *pMe = (SampleApp*)cxt;

if(evt == NE_PPP) {
if(dwData == NET_PPP_CLOSING || dwData ==
NET_PPP_CLOSED) {
// 标记设置为 false,说明套接字已坏
pMe->m_SocketState = FALSE;

// 清除网络和套接字
ReleaseNetAndSocket(pMe);
}
}
if(evt == NE_SO_CLOSING || evt == NE_SO_CLOSED) {
pMe->m_SocketState = FALSE;
ReleaseNetAndSocket(pMe);
}

if(evt == NE_SO_CONNECTED) {
pMe->m_SocketState = TRUE;
}
return;
}

// 使用套接字之前检查它的状态
static void RoadWarriorApp_ReadCB(void *cxt) {
int rc;
SampleApp *pMe = (SampleApp *)cxt;
if(pMe->m_SocketState == FALSE) { //socket is bad
ReleaseNetAndSocket(pMe);
ShowMainMenu(pMe);
return;
}

rc = ISOCKET_Read(piSock, (byte*)buf, sizeof(buf));
... ... ...
... ... ...
}



使用 INETMGR_InEvent() 注册网络和套接字事件时,必须在终止应用程序前使用 INETMGR_OnEvent() (bRegister = FALSE) 取消注册。

请注意:电话超出覆盖范围后,套接字无法使用。必须在下次要求时释放您的 Isocket 并重新实例化。INetMgr 在用于下次网络操作前不必释放和重新实例化,不过这样做也无妨。

要处理异步 (UDP) 数据通信可能错误的套接字,请注册网络/套接字状态的变化(如上文第 2 条所述)。
通过关闭打开的套接字终止数据呼叫时,为什么电话仍显示数据呼叫正在进行?
关闭最后一次连接的套接字后,BREW 会在终止 PPP 连接前等待一定的延迟时间。因此,出现了延迟的呼叫正在进行显示。

默认延迟时间为 30 秒。要更改延迟时间,请使用 INETMGR_SetLinger()。
服务中断时(尽管试图连接)或服务器不响应时为什么我的连接回调不超时?
这是由于 BREW 版本 1.0.1 SDK 中的缺陷引起的 - 在以下情况下不调用连接回调:
试图连接时服务中断。
服务器不响应

作为暂时解决办法,您应该使用与回调相关的计时器。如果 30 秒内不发生连接回调,则应该引起连接超时。

例如:
// 初始化 pMe->connectCBInvoked = FALSE;
// 调用 ISOCKET_Connect() 之前将计时器设置为 30 秒钟
ISHELL_SetTimer(pMe->a.m_pIShell, 30000, ConnectTimeout_CB, (void *)pMe);
ISOCKET_Connect(pMe->m_pISocket, nodeINAddr, AEE_htons(USAGE_TEST_PORT),
SampleApp_ConnectCB, pMe);

// 设置说明已调用连接 CB 的标记
void SampleApp_ConnectCB(void *cxt, int err) {
SampleApp *pMe = (SampleApp*)cxt;

// 设置说明已调用连接 CB 的标记
pMe->connectCbInvoked = TRUE;
if (err) {
DisplayOutput((IApplet*)pMe, 3, "Connect failed!");
return;
}
DisplayOutput((IApplet*)pMe, 3, "Connected!");
}

// 计时器过期时,检查是否已调用连接 CB
static void ConnectTimeout_CB(void* cxt) {
SampleApp *pMe = (SampleApp *)cxt;

if(pMe->connectCbInvoked == FALSE) {
// 在 30 秒内未调用回调 - 取消连接回调
ISOCKET_Cancel(pMe->m_pISocket, 0, 0);
DisplayOutput((IApplet*) pMe, 3, "Connection timed out");
}
else {
// 已调用回调。将标记设置为默认值 FALSE
pMe->connectCbInvoked = FALSE;
}
}

使用 ISHELL_CreateInstance 进行初始化时为什么我的 INetMgr 对象为 NULL?其它对象(如 IDisplay 或 IGraphics)已经正确初始化。
请参阅我试图创建 Net Manager 的实例 (AEECLSID_Net) 时为什么 ISHELL_CreateInstance 返回 ECLASSNOTSUPPORT?(技术 FAQ 7o)。
调用 INETMGR_GetHostByName() 执行 DNS 查找时应该采取哪些预防措施?
必须在所有退出途径中使用 CALLBACK_Cancel() 确保您的应用程序取消未完成的 DNS 回调(如果有)。这包括中止应用程序和停止应用程序的情况(都通过结束键和清除键)。

例如,如果您使用了以下代码来查找 DNS 地址:
CALLBACK_Init(&pMe->cbkLookup, GetHostByNameCB, pMe);
INETMGR_GetHostByName(pINetMgr,&pMe->dnsresult, hostName,
&pMe->cbkLookup);

您应该使用以下代码在所有退出途径中取消回调:
// 检查是否可以取消回调。如果 NULL
// 已发生,无法取消。
if(pMe->cbkLookup.pfnCancel != NULL) {
CALLBACK_Cancel(&pMe->cbkLookup);
}

回 调取消代码应该并入中止事件处理程序代码 (EVT_APP_SUSPEND)、应用程序的 FreeAppData 函数(在应用程序停止时,即接收 EVT_APP_STOP 事件时调用)和清除键按键事件处理程序代码 (EVT_KEY 和按键代码 AVK_CLR) 中。有关详细信息,请阅读下文:中止和恢复应用程序及下面的 FAQ:使用电话的“End(结束)”键关闭小程序和使用“Clear(清除)”键关闭小程序有何区别?。

请注意:如果您的应用程序没有正确地取消其未完成的 DNS 回调,则不会通过 TRUE BREW 测试。可能表示您的应用程序未取消未完成的 DNS 回调的某些故障现象如下:
电话在启动应用程序时失效
电话在启动应用程序时显示“Please Re-insert Battery(请重新插入电池)”错误

两者都表示以前余兴的应用程序未取消其未完成的 DNS 回调。
对于我可以设置的应用程序网络延迟时间的值有限制吗?
提 供 INETMGR_SetLinger() 函数以使开发者灵活设计他们的应用程序。但是,建议不要变更 OEM 选定的默认延迟时间(通常为 30 秒钟),除非您的应用程序有非改不可的理由。变更任一上目录中的默认延迟时间都可能导致您的应用程序无法通过 TRUE BREW 测试,或导致运营商拒收您的应用程序。
BREW 支持哪些图像格式?
BREW 支持颜色深度达到运行设备上提供的值的任何 BMP 文件。BREW 目前尚不支持 GIF 和 JPEG 图像。目前,您需要将 GIF 和 JPEG 图像转换为 BMP 图像。BREW SDK 版本 1.1 将支持 PNG 格式和 BREW Compressed Image (BCI) 格式。

在 1.1 之前的 SDK 版本中,模拟器只能模拟 1 位、4 位和 8 位颜色深度的 BMP。在版本 1.1 中,模拟器仅可以显示 2 位颜色深度的 BMP。
彩色和单色电话上的透明色是什么?
紫色是彩色色被的透明色。白色是单色和 4 级灰度设备上的透明色。
如何将光标移动到文本输入的末尾?
可以连续对字符串调用 ITEXTCTL_HandleEvent() 方法 n 次(其中,n 是字符串的长度)以将光标移动到字符串的末尾。

例如:
while(len) {
ITEXTCTL_HandleEvent(pMe->m_pIText, EVT_KEY,
AVK_RIGHT, dwParam);
len--;
}


请 注意:当前,这在 Kyocera 3035 上不工作,除非将 dwParam 设置为 10(相对于 AVK_RIGHT)。Kyocera 正在试图解决此问题,以便无需对 dwParam 进行明确设置。设置 dwParam = 10 对 Sharp Z-800 手持设备没有不良影响。

BREW SDK 版本 1.2 将增加一个新的 API - ITEXTCTL_SetCursorPos(),用来设置文本控件的光标位置。
如何创建有水平和垂直滚动条的文本控件?
如果文本大于文本控件,则会显示一个垂直或水平滚动条,使您可以使用方向键在文本中定位。
使用 IDISPLAY_BitBlt() 绘制位图时为什么会收到内存错误,如“memheap.c 0696”?
请确保释放 CONVERTBMP 分配的内存。检查 CONVERTBMP 的最后一个布尔型参数。如果为 True,则重新分配已完成,必须使用 SYSFREE 来释放内存。

例如:
pBmp = CONVERTBMP (pDataBytes, &imageInfo, &bVal);
IDISPLAY_BitBlt (pIDisplay, xDest, yDest, cxDest,
cyDest, pBmp, xSrc, ySrc, dwRopCode);
IDISPLAY_Update (pIDisplay);
if(bVal) //只有完成 realloc 后才能释放
SYSFREE (pBmp);


请参阅 API 参考文档中的 CONVERTBMP 助手函数说明。若需示例代码,可以参阅 IDisplay 用法示例。
如何不让标题区域在 Istatic 上显示?
SDK 版本 1.0 目前无法对标题区域进行限制。BREW 版本 1.1 中已增加此功能。
如何确定在电话上显示应用程序名的字符限制?
不同的电话具有不同的显示特性,因此对此问题没有唯一答案。您可以通过比较应用程序名的宽度和显示宽度来确定应用程序名是否适合电话显示。

使用 IDISPLAY_MeasureText() 确定应用程序名字符串的像素宽度。使用 ISHELL_GetDeviceInfo() 确定屏幕的像素宽度。
BREW 是否支持动画?
BREW SDK 版本 1.0 支持动画 BMP。这是通过并列放置所有帧并使用带 IPARM-CXFRAME 标记的 IIMAGE_SetParm 指定每帧的宽度实现的。请参阅 Examples 目录中的 IIMAGE 示例。

BREW SDK 版本 1.1 增加了对 BREW Compressed Image (BCI) 动画的支持。BCI 文件包含一个或多个压缩的小图像,每个图像都有一个以毫秒指定的持续时间。持续时间表示每一幅图像被该系列下一幅图像取代之前可显示的时间。您可以使用 BREW SDK 版本 1.1 随附的 BCI 编辑工具来创建 BCI 文件。有关详细信息,请参阅 SDK 随附的“使用 BREW Compressed Image 编辑工具”文档。
如何按时间控制动画?
执行此操作的一种方式是使用 IImage 接口并设置动画速度 (IImage_SetParm)。下面是将动画速度设置为 750ms 的一个示例:
IIMAGE_SetParm(pMe->m_pIImage, IPARM_RATE, 750, 0);

您也可以使用计时器来触发图像显示函数。使用 ISHELL_SetTimer() 设置计时器:
ISHELL_SetTimer(pMe->a.m_pIShell, TIMER_RATE,
(PFNNOTIFY)(ManipulateBitmap), pMe);


TIMER_RATE ms 失效时,将触发 ManipulateBitmap 函数。在该函数中,您可以操纵图像。有关详细信息,请参阅 Brick Attack and Space 示例。
文本输入的多次按键模式和 T9 文本输入模式有何区别?
在 多次按键模式下,可以通过多次按键来指定一个字母。例如,要指定字母“r”,请按数字“7”三次。在 T9 模式下,每个字母仅需按键一次。T9 文本输入用于确定与输入数字序列匹配的最常用字。如果字不止一次与该序列匹配,则选择该最常用字并且能够滚动到下一个最常用字。

默认文本输入模式是多次按键。模拟器不支持 T9 文本输入模式。
如何绘制特定颜色的线段?
IDISPLAY_DrawHLine() 和 IDISPLAY_DrawVLine() 始终绘制黑色线段。因此,将 CLR_USER_LINE 设置为所需颜色,然后调用 IDISPLAY_DrawHLine() 或 IDISPLAY_DrawVLine() 无效。

这两个 IDISPLAY 宏的定义如下。要绘制除黑色外的颜色的线段,请使用宏定义中所含的代码并更改为所需填充颜色。
#define IDISPLAY_DrawHLine(p,x,y,len) /
{AEERect rc;SETAEERECT(&rc,(x),(y),(len),1); IDISPLAY_FillRect((p),&rc,
RGB_BLACK);}

#define IDISPLAY_DrawVLine(p,x,y,len) /
{AEERect rc;SETAEERECT(&rc,(x),(y),1,(len)); IDISPLAY_FillRect((p),&rc,
RGB_BLACK);}


如何创建对话框?
1. 使用 BREW 资源编辑器创建对话框。也可以手动构造对话框,即在您的应用程序中创建数据结构,再定义对话框的内容。有关详细信息,请参阅《BREW 资源编辑器指南》文档和《BREW SDK 用户指南》中的“使用对话和控件”一节)。

提示:如果使用 bmp 文件,请确保可以支持 bmp 格式,而且 bmp 文件是一个有效的完全路径,全部采用小写形式。

2. 一旦创建对话框,它将有一个资源 ID 和资源文件(.bar 文件)。使用 ISHELL_CreateDialog() 创建对话框:
ISHELL_CreateDialog(pMe->a.pIShell, SAMPLEAPP_RES_FILE,
RESOURCE_ID, NULL);
// SAMPLEAPP_RES_FILE 为资源文件 (.bar file),
// RESOURCE_ID 为资源中指定的资源 ID
// 编辑器


3. 在应用程序处理程序函数中处理以下事件(至少返回 TRUE):
case EVT_DIALOG_START:
return TRUE;
case EVT_DIALOG_INIT:
return TRUE;
case EVT_DIALOG_END:
return TRUE;


4. 对话框对象不再需要时调用 ISHELL_EndDialog。
ISHELL_EndDialog(pMe->a.pIShell);

有关详细信息,请参阅《BREW SDK 用户指南》中的“使用对话和控件”一节。
我的菜单比屏幕大。如何创建滚动条?
要绘制菜单的屏幕矩形(由 IMENUCTL_SetRect 指定)必须超出屏幕高度至少单个菜单项的高度。否则,菜单项将被剪切且不显示任何滚动条。
如何将图像添加到 IMENUCTL 的项目中?
对于 CtlAddItem 结构,可以使用 IMENUCTL_AddItemEx 方法。请参阅 Examples 目录中的 IMENUCTL 示例。
是否可以从与我的文本控件相关的软键菜单中删除多次按键项目?
可以。您可以按照下述步骤将其删除:
使用 ITEXTCTL_SetSoftKey() 将软键菜单与您的文本控件关联起来
调用 IMENUCTL_DeleteAll() 来删除多次按键项目
使用 IMENUCTL_AddItem() 添加项目
是否可以获取/操纵设备的调色板信息?
不可以。调色板由生产商进行硬编码,随设备不同而不同。
是否可以直接修改显示缓冲区?
不可以。BREW 无法访问这些缓冲区,而且显示数据使用生产商的专有格式存储。
如何在我的文本控件中实现符号输入?
通过按某键(通常是 1)多次即可使用有限的符号。此符号集取决于 OEM。在 Kyocera 3035 上,按“1”键多次可以出现以下符号:.&@,-':;?/"()。在 Sharp Z-800 上,按“1”键多次可以出现以下符号:.,@:!?/。

要在您的文本控件中实现较大的宽度,您必须使用 ITEXTCTL_SetSoftKeyMenu() 将您的文本控件与软键菜单关联起来。使用此方法可以提供的符号集是:-.&'()_!?*#%":+<>=??""//@,[]^;。

ITEXTCTL_SetSoftKeyMenu() 将项目添加到软键菜单中,允许用户更改文本输入模式(该项目的文本字符串是当前所选模式 - 例如,多次按键)。收到该命令,文本控件显示一个菜单,允许用户选择新的文本输入模式。用户选择新模式后,文本控件重新激活,用户继续输入文本。输入文本 时,用户可以按 Select(选择)键脱离文本编辑模式并激活软键菜单。软键菜单活动时,用户可以按向上键返回编辑模式,不必进行菜单选择。

下面是如何在您的文本控件中实现多个输入模式的一个示例:
// 创建文本控件
ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_TEXTCTL,
(void **)&pMe->m_pIText);

if (pMe->m_pIText) {
// 创建软键菜单
ISHELL_CreateInstance(pMe->a.m_pIShell, AEECLSID_SOFTKEYCTL,
(void **)&pMe->m_pISoftKey);
if (!pMe->m_pISoftKey) {
ITEXTCTL_Release (pMe->m_pIText);
pMe->m_pIText = NULL;
return FALSE;
}
}
else {
return FALSE;
}

// 设置软键菜单矩形
IMENUCTL_SetRect(pMe->m_pISoftKey, &softKeyRect);

// 设置文本控件标题、大小、属性和矩形
ITEXTCTL_SetTitle(pMe->m_pIText, NULL, NULL, titleString);
ITEXTCTL_SetMaxSize(pMe->m_pIText, 100);
ITEXTCTL_SetProperties(pMe->m_pIText, TP_FRAME | TP_MULTILINE );
ITEXTCTL_SetRect(pMe->m_pIText, &rect);

// 初始化文本控件的软键菜单。
ITEXTCTL_SetSoftKeyMenu (pMe->m_pIText, pMe->m_pISoftKey);

// 将软键和文本控件均设置为激活状态。
IMENUCTL_SetActive(pMe->m_pISoftKey,TRUE);
ITEXTCTL_SetActive (pMe->m_pIText, TRUE);


在应用程序的 HandleEvent 函数中,您必须处理 EVT_KEY 和 EVT_COMMAND 事件,如下所示:
case EVT_COMMAND:
// 处理 EVT_COMMAND 事件 - 输入模式更改
if((pMe->m_pIText != NULL) && ITEXTCTL_HandleEvent(pMe->m_pIText,
eCode, wParam, dwParam)) {
return TRUE;
}
return FALSE;
case EVT_KEY:
if ((pMe->m_pIText != NULL) && ITEXTCTL_HandleEvent(pMe->m_pIText,
EVT_KEY, wParam, dwParam)) {
//由文本控件处理的事件
return TRUE;
}

if(pMe->m_pISoftKey != NULL && IMENUCTL_HandleEvent(
pMe->m_pISoftKey, EVT_KEY, wParam, dwParam)) {
//由菜单控件处理的事件
return TRUE;
}
return FALSE;

如何对数据库记录排序?
当前没有 API 可用于对数据库记录排序。正在进行这方面的工作,将在未来版本中增加该功能。
如何连接到外部数据库?
BREW 不提供连接到外部数据库(如 Oracle、MS SQL 等)的客户机端模块。您可以使用 BREW 提供的标准套接字机制,编写自己的客户机商模块连接到外部数据库。
文件名的最大允许长度(字符数)是多少?
此限制由 OEM 决定,不由 BREW 决定。目前,将文件名的最大字符数设置为 40 个字符(在 Sharp 电话上)和 60 个字符(在 Kyocera 电话上)。此限制针对完全文件路径,即文件名及其目录结构的长度。

数据库中可以存储多少字节?
这取决于设备。数据库文件存储在 EFS 上,因此,它取决于设备上的文件系统空间。
IDBRECORD_Update() 如何工作?
如 果数据库记录的旧内容与其新内容一样大,则将用新内容更新该记录。如果新内容与旧内容不一样大,则将新记录添加到数据库中,将旧记录标记起来以便删除。标 记起来以便删除的记录数达到预定义的 OEM 限制(在 Kyocera 3035 上限制为 5)时,记录将被物理删除。另外,关闭然后再打开有标记起来以便删除的记录的数据库时,标记起来以便删除的记录将在数据库打开时被物理删除。
请解释 BREW 使用的数据库索引编制方案。
在 BREW SDK 版本 1.1 中,数据库记录在数据库生命周期中保持同一索引(记录 ID),除非被删除。不过,在 BREW SDK 版本 1.0.1 中,情况不是这样。

数 据库记录被删除时,该记录被标记起来,以便删除。标记起来以便删除的记录数达到预定义的 OEM 限制(在 Kyocera 3035 上为 5)时,记录将被物理删除。关闭再打开数据库时,这些记录也会被物理删除(记录在打开时删除)。在 BREW SDK 版本 1.0.1 中,当记录被物理删除时,已删除记录后面的所有数据库记录的索引将更改。因此,在版本 1.0.1 中,数据库记录索引不是唯一的。这已在版本 1.1 中修复。
如何在小程序多次重新安装间保存状态?
您可以使用文件或数据库保存小程序多次重新安装间的状态。在文件/数据库名中使用 Class ID 和小程序版本号组合可以唯一标识该小程序。

如果您想在多个小程序间共享该信息,则请在共享目录中创建此文件/数据库。有关详细信息,请参阅如何能在多个模块间共享资源?。

您 也可以使用 ISHELL_SetPrefs() 和 ISHELL_GetPrefs() 将包含小程序状态的结构存储在小程序的 Prefs 数据库中。有关如何使用 ISHELL_SetPrefs() 和 ISHELL_GetPrefs() 的信息,请参阅 ishellusage 示例。
如何提交 1.x 应用程序中的数据库文件?
在 BREW 1.x 中,应用程序提交包中包含的每个文件都必须有一个扩展名(有关详细信息,请参阅 http://www.qualcomm.com/brew/sch/developer/support/kb/235.html)。这将影响那些作为其提交包一部分包含数据库文件的应用程序。

在 Emulator 和大多数手持设备上,创建名为 "foo" 的数据库将会产生下面两个文件:一个为无扩展名的数据库文件 (foo),一个为带有 idx 扩展名的索引文件 (foo.idx)。由于每个 BREW 1.x 文件都必须带有扩展名,所以这在 BREW 1.x 中会产生问题。由于 BREW 1.x 不允许文件名包含多个扩展名(1.x 中不可能存在 foo.db.idx),因此不可能产生带扩展名的数据库名称(如 "foo.db")

解决办法是在提交数据库之前,使用虚扩展名(如 .dbf)重命名该数据库。因此,在提交前 Foo 可能会被重命名为 foo.dbf。然后,在运行期间,应用程序需要去掉虚扩展名来重命名数据库名称,(在运行数据库操作之前,将 foo.dbf 更改为 foo)。

解决办法是在提交数据库之前,使用虚扩展名(如 .dbf)重命名该数据库。因此,在提交前 Foo 可能会被重命名为 foo.dbf。然后,在运行期间,应用程序需要去掉虚扩展名来重命名数据库名称,(在运行数据库操作之前,将 foo.dbf 更改为 foo)。

例如:

// 检查是否存在 foo.dbf

if(IFILEMGR_GetInfo(pIFileMgr, "foo.dbf", &fInfo) == SUCCESS) {

//通过去掉扩展名来重命名

if(IFILEMGR_Rename(pIFileMgr, "foo.dbf", "foo") == SUCCESS) {

// 重命名成功

// 继续进行数据库操作:IDATABASE_Open(.., "foo", ..)

}

}


请注意,由于解决办法中已重命名数据库文件,所以提交时不应为数据库文件签名(使用 BREW AppSigner 工具)。

在 Kyocera 3035 上,创建的数据库 (foo) 为 foo.dat 和 foo.idx。在这种情况下,不需要对您的代码作特殊处理。IDATABASE_Open(.., "foo", ..) 将工作。请记住,无法先在模拟器上创建数据库(无扩展名),然后在传输到电话时添加 .dat 扩展名。在电话上将无法打开该数据库。

在 BREW 2.0 中,这不再是一个问题,因为:
a) 文件名允许使用多个扩展名。
b) 下载时只有 MIF 文件才会被截断和附加 ~ ,因此可以接受缺少扩展名的文件名。
IsoundPlayer 似乎不工作。为什么?
使 用 ISoundPlayer 接口要求电话上有多媒体支持(Qualcomm 的 Compact Multimedia Extension - CMX 或类似)。 Kyocera 3035 不是启用 CMX 的。Sharp Z-800 Build .97 以上版本是启用 CMX 的,支持 MIDI,但不支持 MP3。

有关支持 MP3 的详细信息,请参阅 BREW 是否支持 MP3 播放?。
如何在音调序列中插入一段静音?
BREW SDK 版本 1.0.1 不支持静音插入。您可以选择与静音相似的音调,或者停止播放一段时间,然后再继续播放。

BREW SDK 版本 1.1 中增加了 AEESoundTone AEE_TONE_SILENCE(相当于静音)。
ISOUND_Vibrate 似乎在我的电话上不工作。为什么?
此功能要求 OEM 支持。实施此功能不要求 OEM。 具体地说,Sharp Z800 不支持振动功能。
BREW 是否支持 MP3 播放?
支持,只要设备带有含 MP3 解码器的芯片组和覆盖多媒体层(CMX 或等效功能), BREW 就支持通过 ISoundPlayer 接口播放 MP3 文件。

有关 ISoundPlayer 的详细信息,请参阅IsoundPlayer 似乎不工作。为什么?。

有关不同设备功能的详细信息,请参阅开发者外联网上的电话资料。

Kyocera 3035 上似乎在以固定音量播放声音文件。什么 API 可以用来控制音量?
使 用 ISOUND_SetVolume() 控制音量。在 AEESoundInfo 数据结构中, 将振铃器/按键蜂鸣声方法设置为 AEE_SOUND_METHOD_RING / AEE_SOUND_METHOD_BEEP,将 eDevice 设置为 AEE_SOUND_DEVICE_HANDSET。
是否可以同时播放多个音调?
Qualcomm 计划在 BREW SDK 版本 2.0 中支持同时播放多个音调。 目前,一次只能播放一个音调。如果第一个音调完成播放前进行调用要求播放第二个音调,则第一个音调会中止。
如何在不超出最大文件数限制的情况下加载多个声音文件?
一种可行的解决办法是将多个声音文件存储为一个文件。您可以将相应的声音剪辑加载到一个缓冲区中,然后使用 ISOUNDPLAYER_SetStream 从该缓冲区播放。
例如:

//打开读取文件并存储到应用程序指针位置。
pMe->m_pIFile = IFILEMGR_OpenFile(pMe->m_pIFileMgr, pMe->MultipleMidiFile, _OFM_READ);

if (!pMe->m_pIFile)
return FALSE;

// 计算 DESIRED_OFFSET(未显示)

if (IFILE_Seek(pMe->m_pIFile, _SEEK_START, DESIRED_OFFSET) != SUCCESS )
return FALSE;

// 该函数异步触发一个 AEE_SOUNDPLAYER_STATUS 回调。
ISOUNDPLAYER_SetStream(pMe->m_pISoundPlayer, (IAStream *)pMe->m_pIFile);

如何获得自 1980 年 1 月 6 日(设备的参考时间)起的秒数?
使用 GET_SECONDS(),如下所示:
uint32 timeInSeconds = GET_SECONDS();


该操作返回 1980 年 1 月 6 日 00:00:00 GMT 至今(已调整为当地时间)的秒数。
如何获得当前日期?
要获得当前日期,请调用 GET_JULIANDATE() 并从已填充的 JulianType 结构提取信息。

例如:
JulianType julianDate;
uint32 timeInSeconds;
uint16 year, month, day;

GET_JULIANDATE(0, &julianDate);

year = julianDate.wYear;
month = julianDate.wMonth;
day = julianDate.wDay;


要用系统时间初始化 IDateCtl,请使用从 JulianType 提取的信息调用 IDATECTL_SetDate()。

例如:
IDATECTL_SetDate(pMe->p_date, julianDate.wYear,
julianDate.wMonth, julianDate.wDay);


GETTIME_MS() 在模拟器上工作,但在电话上不工作。为什么?
SDK 版本 1.0.1 和 1.1 中的 GETTIME_MS() 和 ISHELL_GetTimeMS() 中有故障。这将在 BREW 的以后版本中修复。同时,您可以使用以下暂时解决办法获取时间:
JulianType julianDate;
uint32 timeInSeconds;
uint16 hour, min, sec;
char tempBuf[30] = {0};

GET_JULIANDATE(0, &julianDate);

hour = julianDate.wHour;
min = julianDate.wMinute;
sec = julianDate.wSecond;

SPRINTF(timeBuf, "Time: %u:%u:%u", hour, min, sec);


GET_JULIANDATE() 在模拟器上工作,但在电话上不工作。为什么?
由 本函数填充的 JulianType 数据结构在模拟器和手持设备上以不同的方式填充 wWeekDay 字段。在模拟器上,wWeekDay 采用值 (0..6),其中,0 = 星期日,6= 星期六。在手持设备上,wWeekDay 采用值 (0..6),其中,0 = 星期一,6= 星期日。

这已在 BREW 1.1 中修复 - 在 BREW 1.1 中,模拟器和手持设备都返回 JulianDate.wWeekDay 值 0..6,其中,0 = 星期一,6 = 星期日。

版本 1.0 的暂时解决办法:

在 BREW 1.0 中,可以使用以下代码确保模拟器和手持设备得到同样的结果。
GET_JULIANDATE(0, &julian);
#ifdef AEE_SIMULATOR
if (julian.wWeekDay == 0)
julian.wWeekDay = 6;
else
julian.wWeekDay --;
#endif

如何为我的 BREW 应用程序获取 class ID?
提 交您的 BREW 应用程序进行 True BREW 测试前,必须使用通过 Web 接口可以获得的 BREW Class ID Generator 获取 BREW Class ID。为了访问本页,贵公司必须购买 VeriSign, Inc. 的 BREW 特定数字证书并授权特定开发者访问 BREW 开发者外联网上的此工具。

应用程序编码和测试(提交前)期间,您可以使用 MIF 编辑器生成一个临时 Class ID(大于 0x02000000)。
我有一个使用 1.0.1.18 BREW SDK 编写的应用程序。但是,我的电话上的 BREW API 是 1.0.2.4。我的应用程序是否会在电话上运行?
它会运行。

试图确定 BREW SDK 版本和电话上的 BREW API 版本间的兼容性时应记住以下要点:

为 BREW SDK 版本 X 编写的应用程序将在手持设备上 BREW API 版本 X 或更高版本上工作。但是,反之不然,即为 BREW SDK 版本 X 编写的应用程序不能在具有早于 X 版本的 BREW API 的手持设备上工作。
TRUE BREW 测试包括哪些内容?由谁进行测试?
由第三方测试试验室来执行兼容性测试。测试的具体内容因应用程序而异,但一般来说,测试将涉及以下内容:
应用程序如何与核心电话功能交互。例如,应用程序如何 处理诸如有即时消息或其它系统警告进入设备而用户正在使用另一个应用程序时情况。
内核应用程序的长期稳定性。向事件队列发送大量的事件,以查看应用程序如何处理它们,看看系统的不稳定性(如崩溃)、内存使用(如峰值)等。
重要的应用程序功能。进行测试以确保您宣称的重要应用程序功能在应用程序中实际存在。 QUALCOMM 不会从最终用户角度测试该功能的“正确性”。
应用程序使用的每个 BREW 功能领域的一组标准测试。 例如,如果应用程序使用 BREW 文件系统调用,则我们将使用标准的文件系统测试套件。 类似地,如果应用程序使用 BREW 网络调用,我们 将使用标准的网络测试套件进行测试。
任何因运营商或生产商而异的外观要求。
与目标设备的兼容性。
与必需附件的交互。
与 QUALCOMM 指定的一小组内核应用程序的互操作性。例如, 如果市场上的大多数电话最终使用特定浏览器,则我们 将测试所提交的应用程序,确保它能与该浏览器较好地互操作。
测试 BREW 应用程序的指导原则 提供有关如何对应用程序进行 True BREW 测试准备的详细信息。

应用程序认证失败的常见原因是什么?
应用程序未通过测试的最常见原因如下所示:

打包:

提交软件应用程序为测试前应如何将您的应用程序打包提供指导原则。 记住所有下载到电话上的文件名都应该采用小写形式。

签名:

通 常,提交应用程序时会出现签名错误的情况。使用 BREW AppSigner 工具为您的应用程序签名时, 记住您必须为 .mod、.mif、.bar(如果有)文件签名。应用程序使用的其它文件可能也要签名。 如果这些您曾签名的“其它”文件之一将在手持设备上进行更改, 则必须在 BREW AppSigner 工具中将它标记为“Gets Modified(已修改)”(例如每次玩游戏时存储比分的数据文件 foo.dat)。您不应该为应用程序特定的支持文档、以前测试签名或 VeriSign 回执签名。

修改已签名文件:

提交前不应修改已使用 BREW AppSigner 工具进行签名的文件。如果在提交进行认证前需要修改已签名的文件, 则应该进行修改,删除现有的签名文件并再次重复签名过程, 以创建一个新的签名文件。

不正确处理中止/恢复:

中止和恢复事件必须予以正确处理。有关详细信息,请参阅下文: 中止和恢复应用程序。

使用 CLR 键退出应用程序时未终止数据呼叫:

必须正确释放网络和套接字资源并取消所有尚未 调用的回调(读取回调、写入回调、连接回调、INETMGR_OnEvent 回调、 GetHostByName 回调等}。如果未能正确完成该网络和套接字清除, 则数据呼叫不会在使用 CLR 键结束应用程序时终止。

选择在配置菜单中显示应用程序且不处理 EVT_APP_CONFIG 事件:

在 某些启用 BREW 的设备上,您可以选择使小程序显示在配置 或设置菜单上,以便设备用户设置小程序的首选项。您可以通过 在 MIF 编辑器中选中“在配置菜单上显示”复选框来进行此设置(“小程序”选项卡的“高级”部分)。 如果从配置菜单中选择您的小程序,则将向它发送一个 EVT_APP_CONFIG 事件。 您的小程序必须处理 EVT_APP_CONFIG 事件,可能会显示一个对话框提示设备用户设置首选项。 如果您不希望在配置菜单中显示您的小程序, 请在您的 MIF 文件中取消选择“在配置菜单上显示”选项。

未取消未完成的 DNS 回调:

在所有退出途径中必须取消未完成的 DNS 回调(如果有)。有关详细信息,请 参阅调用 INETMGR_GetHostByName() 执行 DNS 查找时应该采取哪些预防措施 FAQ。

连接回调未激活:

BREW SDK 版本 1.0.1 中有一个问题,即在以下情况下连接回调不会超时:
试图连接时找不到覆盖范围,或者
服务器不响应

请参阅服务中断时(尽管试图连接)或服务器不响应时 为什么我的连接回调不超时 FAQ。

未验证文件句柄的有效性:

使用 IFILEMGR_OpenFile 创建文件时,访问该文件之前,应该检查返回的文件指针 是否为 NULL 值。如果文件无法打开,则文件指针为 NULL。 无法打开文件的可能原因包括 1) 手持设备已达到最大文件数限制;和 2) EFS 已满。
已下载的应用程序包中包含子目录:
应用程序下载服务器 (ADS) 具有一个平面文件结构, 包含子目录的应用程序无法打包以在 ADS 上部署。 应用程序可以在运行时创建子目录; 只是在初始下载时不能包含子目录。

不包括 MIF 文件中要求的所有图像,或提交的图像尺寸不正确: 所有 BREW 应用程序均要求 包含以下三种应用程序图像 - 图标、缩略图 和图像,每种都有特定的像素尺寸。请参阅“我的应用程序中需要包含的图像以及其大小要求是什么?”

更改网络延迟时间:
更改应用程序的网络延迟时间可能会导致应用程序无法通过 True BREW 测试,或者致运营商拒用您的应用程序。请参阅 FAQ,对于我可以设置的应用程序网络延迟时间的值有限制吗?

超出每个应用程序限制的最大文件数:
您应用程序中包含的文件数超出了运营商的指定数量,可能会导致应用程序无法通过 True BREW 测试,也可能导致运营商拒绝您的应用程序。请参阅 FAQ,对于我的应用程序可以随附的文件数有限制吗?

提交无应用程序扩展名的文件:
在 BREW 1.x 中,应用程序提交包中包含的每个文件都必须有一个扩展名(有关详细信息,请参阅 http://www.qualcomm.com /brew/sch/developer/support/kb/235.html)。这将影响那些作为其提交包一部分包含数据库文件的应用程序。有关“ 作为应用程序包的一部分包含数据库文件的解决办法”的详细信息,请参阅至 9h 的链接。应用程序包中的每个 1.x 文件都需要有一个扩展名。如何提交 1.x 应用程序中的数据库文件?

提交应用程序包中的零字节文件:
由于 BREW 1.x 缺陷,Mobile Shop(移动商店)有时无法下载包含零字节文件的应用程序。请确保您的应用程序包中不包含任何零字节文件。零字节文件只会在下载时引起问题 - 在运行时期间可能会产生零字节文件。

在应用程序的 MIF 文件进行了 TAPI 状态通知注册:
由 于 BREW 1.0 缺陷,因此无法在应用程序的 MIF 文件中注册 TAPI 状态通知 (NMASK_TAPI_STATUS)。这是因为在 MIF 文件中为 TAPI 状态注册的应用程序被禁用时会发生错误:在启动 Mobile Shop(移动商店)连接后,BREW 会尝试通知应用程序(因为数据调用已开始并引发一个 TAPI 状态更改事件)。因为已禁用该应用程序,所以 BREW 会尝试重新启用它,这样会再次启动 Mobile Shop(移动商店)连接。结果产生一个不停重新启动 Mobile Shop(移动商店)的循环。该缺陷已在 BREW 1.1 及以后的版本中修正。对于该问题没有解决办法 - BREW 1.0 应用程序不能在它的 MIF 文件中注册 TAPI 状态更改事件。要在运行应用程序时获得 TAPI 状态更改事件的通知,可以调用 ISHELL_RegisterNotify()。



14c.问:如何将多个模块打包成单个 True BREW 测试提交?
答:如果您在提交由多个模块组成的应用程序, 请按下述说明安排 zip 文件的结构:

多模块提交说明:

每个 "mod" 文件必须有一个唯一名称。开发者应该将每个 "mod" 文件放在 ARM 目录下的独立子目录中。每个子目录都应该有 自己的 "mod" 和 "mif" 文件且已单独签名。子目录名 与关联的 "mod" 文件应该相同。例如:
ARM/test1/

test1.mod

test1.mif

test1.sig

test1.bar

ARM/ test2/

test2.mod

test2.mif

test2.sig

等等...


ARM 目录使用的同一目录结构也应用于 WIN 目录。 应用程序的 Windows 版本也 用于测试且应该依据于电话所用的同一模块。



使用扩展类的小程序是否需要将权限级别设置为“全部”才能成功运行?
要加载扩展,不需全部权限。请确保在您的小程序类的 MIF 文件和扩展类的 MIF 文件中遵守下面的指导原则?

1. 在扩展类的 MIF 文件中:
扩 展的 Class ID 应在导出类下的“扩展”选项卡中列出。它不应在“小程序”选项卡上“小程序信息”下列出。如果在“小程序信息”下列出,则 BREW 将认为 Class ID 属于小程序。只有系统可以使用 ISHELL_CreateInstance() 创建小程序的实例。
2. 在小程序类的 MIF 文件中:
在“小程序信息”下列出小程序 Class ID。
选择必需的权限级别(“常规”选项卡)。
在“依存”选项卡中列出扩展类。
BREW SDK 1.0.1 随附的 ExtUse 和扩展示例在 MIF 文件中包含以下故障:
全部权限已检查
扩展的 Class ID 在“导出类”和“小程序信息”下列出
这已在 1.1 SDK 中修复。
ISHELL_CreateInstance() 在试图创建扩展类的实例时为什么返回 EPRIVLEVEL?
请参阅使用扩展类的小程序是否需要将权限级别设置为“全部”才能成功运行?FAQ。
使用 Mobile Shop(移动商店)删除取决于某个模块的所有应用程序时,有时发现通用模块被删除。为什么?
如果应用程序被连续删除且在应用程序删除期间不退出 Mobile Shop(移动商店),则说明 Mobile Shop(移动商店)中有故障引起了这种行为。

例 如,假定已加载了两个对一个通用模块有依存关系的应用程序。如果您使用 Mobile Shop(移动商店)连续删除了这两个应用程序(删除期间不退出 Mobile Shop(移动商店)),则会看到该通用模块仍然存在。但是,如果在删除期间退出并重新启动 Mobile Shop(移动商店),则会同时删除通用模块。这是可以预期的行为。

通用模块是扩展类。有关使用扩展的详细信息,请参阅 SDK 随附的 ExtUse 和扩展示例和下面的 FAQ 使用扩展类的小程序是否需要将权限级别设置为“全部”才能成功运行?。
请详细说明 BREW 权限级别。
权限级别通过控制对 API 类的访问提供附加安全性。小程序必须具有相应的权限级别才能使用特定的类。小程序的权限级别在其相应的 MIF 中指定。MIF 是通过 MIF 编辑器创建和维护的。

为了使用 Ifile、IfileMgr、Idatabase、IDBMgr、IDBRecord、ISound 和/或 ISoundplayer API 类,小程序必须具有“文件”权限级别。

为了使用 INetMgr 和/或 ISocket ISoundplayer API 类,小程序必须具有“网络”权限级别。

如果未选择相应的权限级别,则该类的 ISHELL_CreateInstance() 会失败。

两个其它权限级别,即“下载”和“全部”显示在 MIF 的“高级权限级别”对话框。这些权限目前不使用,只能由运营商或设备生产商使用。如果您是应用程序开发人员,选择这些权限级别可能会引起应用程序无法通过 TRUE BREW 测试。
为什么在调用 ISHELL_CreateInstance() 时收到 EPRIVLEVEL 21 错误?
这表明您没有创建特定类所需的权限。务必检查应用程序的 MIF 文件中的必需权限(“常规”选项卡)。请参阅请详细说明 BREW 权限级别 FAQ。

如果从小程序 (class_b) 中调用一个非小程序类 (class_a),则必须执行以下操作才能具有必需的权限:
在 class_b 的 mif 文件中,指定 class_a 的外部 class ID(MIF 编辑器中的“依存”选项卡)。
在 class_a 的 mif 文件中,必须导出其 Class ID(MIF 编辑器中的“扩展”选项卡)。
有关详细信息,请参阅 ISHELL_CreateInstance() 在试图创建扩展类的实例时为什么返回 EPRIVLEVEL? FAQ。
可以从何处找到 ISHELL_CheckPrivLevel() API 的各种权限级别 (wPrivWant) 的定义?
权限级别在 API 参考文档的“AEE 权限级别”下进行了说明。SDK 版本 1.1 中没有这些权限级别的定义。这是 SDK 版本 1.1 中的故障。请创建一个具有以下定义的头文件并将该头文件放在您的项目中:
#define PL_FILE (0x0001) // IFileMgr, IDBMgr
#define PL_NETWORK (0x0002) // INetMgr
#define PL_TAPI (0x0004) // ITAPI (简单)权限
#define PL_WEB (0x0008) // IWeb 权限(如果为 PL_NETWORK 也同样适用)
#define PL_DOWNLOAD (0x0020) // IDownload
#define PL_SHARED_WRITE (0x0040) // 写入权限 - 共享目录
#define PL_POS_LOCATION (0x0080) // 定位权限 (ISHELL_GetLocation)
#define PL_RINGER_WRITE (0x0100) // 写入权限 - 振铃器目录
#define PL_ADDRBOOK (0x0200) // 访问地址簿
#define PL_SECTORINFO (0x0400) // GetSectorInfo
#define PL_SYSTEM (0xffff)


在版本 2.0 中,权限级别定义将纳入 AEE.h 中。
BREW 如何处理不同语言的应用程序本地化?
我们建议对于包含语言的任何内容(位图、字符串和对话框),都应使用资源文件并按需在程序中加载所需项目。采用这种方式,您可以避免源代码中的硬编码项目,对于每种语言单独创建资源文件而且不必修改和编译源代码。

有关应用程序韩文本地化的详细信息,请参阅如何在模拟器和手持设备上显示韩文字符? FAQ。

如何在模拟器和手持设备上显示韩文字符?
在当前可用的韩文 BREW 手持设备上,始终将语言设置为韩文,字体是 KSC5601。您可以使用以下步骤进行更改。

设置模拟器上显示的语言和字体
运行 BREW Device Configurator,然后打开所用设备的 QSC 文件(File(文件)> Open(打开))。
单击“型号”字段右侧的“详细资料”按钮,显示“设备属性”对话框。
根据您使用的 BREW SDK 版本,进入下面几部分之一,完成相应步骤。
BREW SDK 1.0:


执行以下操作之一:
对于韩文 KSC5601,从“语言”下拉列表中选择“韩文”。
对于韩文 Unicode,从“语言”下拉列表中选择“韩文”或“英文”。
在“正常字体”和“大字体”字段中,单击“设置”,选择希望使用的韩文字体和磅值。
单击“确定”。
BREW SDK 1,1:
单击“语言和字体”选项卡。
执行以下操作之一:
对于韩文 KSC5601,从“语言”下拉列表中选择“韩文”,从“编码”下拉列表中选择“KSC5601”。
对于韩文 Unicode,从“语言”下拉列表中选择“韩文”或“英文”,从“编码”下拉列表总选择“Unicode”。
在“正常字体”和“大字体”字段中,单击“设置”,选择希望使用的韩文字体和磅值。
单击“确定”。
在手持设备上显示文本

您需要使用 IDISPLAY_DrawText() 和 AEEChar 字符串。下面的步骤说明了需要在 BREW 资源编辑器和 C/C++ 源代码中作出哪些更改,才能使用 KSC5601 和 Unicode 格式创建 AECHAR 字符串。

使用 KSC5601 格式显示韩文字符串
启动 BREW 资源编辑器。
右键单击“字符串”图标,选择“新建字符串”。“字符串资源”对话框打开。
在“资源名称”字段中,键入一个 ID,如 IDS_STRING_KSC。
从“字符串格式”下拉列表中,选择“KSC5601”。
在“值”字段中,键入韩文文本字符串。
单击“确定”。
在 C/C++ 源代码中作以下更改:
使用 ISHELL_LoadResString 加载字符串。
**示例代码**

AECHAR szBuf[30] = {0};
ISHELL_LoadResString(pMe->a.m_pIShell, APP_RES_FILE,
IDS_STRING_KSC, szBuf, sizeof(szBuf));
IDISPLAY_DrawText(pMe->a.m_pIDisplay, AEE_FONT_NORMAL,
szBuf, -1, pMe->m_cxWidth/5, pMe->m_cyHeight/8, 0, 0);


用 KSC5601 代码输入正常字符(无符号字符)串。
使用 STR_EXPAND() 将正常字符串转换为 AEEChar 字符串。
**示例代码**
AECHAR szBuf[30] ;
const unsigned char szhello[] ="";
STREXPAND(szhello,10,szBuf,30);
IDISPLAY_DrawText(pMe->m_pIDisplay, AEE_FONT_BOLD,
(AECHAR *) szBuf, -1, 0, 0, 0, IDF_ALIGN_CENTER |
IDF_ALIGN_MIDDLE);

使用 Unicode 格式显示韩文字符串
启动 BREW 资源编辑器。
右键单击“字符串”图标,选择“新建字符串”。“字符串资源”对话框打开。
在“资源名称”字段中,键入一个 ID,如 IDS_STRING_UNI。
从“字符串格式”下拉列表中,选择“Unicode”。
在“值”字段中,键入韩文文本字符串。
单击“确定”。
在 C/C++ 源代码中作以下更改:
使用 ISHELL_LoadResString 加载字符串。
**示例代码**
AECHAR szBuf[30] = {0};
ISHELL_LoadResString(pMe->a.m_pIShell, APP_RES_FILE,
IDS_STRING_UNI,szBuf, sizeof (szBuf));
IDISPLAY_DrawText(pMe->a.m_pIDisplay, AEE_FONT_NORMAL,
szBuf, -1, pMe->m_cxWidth/5, pMe->m_cyHeight/8, 0, 0);

用韩文 Unicode 输入 AEEChar 字符串。需要输入正确的 Unicode,因为韩文 Windows 使用 KSC5601 进行韩文文本输入。 或者
使 用KSC5601 到 Unicode 转换将 [KSC5601 code Korean character (unsigned char) string] 转换为 [Unicode Korean AEEChar string]。因为此方法要求 BREW 手持设备的内存过多,所以不推荐采用此方法。
**示例代码**
AECHAR szBuf[30] ;
const unsigned char szhello[] ="";
STREXPAND(szhello,10,szBuf,30);
IDISPLAY_DrawText(pMe->m_pIDisplay, AEE_FONT_BOLD,
(AECHAR *) szBuf, -1, 0, 0, 0, IDF_ALIGN_CENTER |
IDF_ALIGN_MIDDLE);

**STREXPAND() 示例代码**
void STREXPAND(const byte * pSrc, int nCount,
AECHAR * pDest, int nSize)

参数:
pSrc 以 Null 结尾的源字符串
nLen 源字符串的长度(字符)
pDest 指向目标缓冲区的指针
nSize 目标缓冲区的大小(字节)

如何在 BREW 中生成随机数?
GET_RAND() 使用当前 CDMA 时间作为随机数元数据。 它确保每次调用 GET_RAND() 时都会生成唯一的随机数序列。
如何获得启用 BREW 的电话?
如果市场上有这种电话,则直接购买。 目前我们正在着手准备地区性电话试验室, 以便为开发者提供地点,供其测试和开发原型电话或适用于全世界各种市场的电话。 我们也在讨论是否有必要向开发者提供电话生产商的其它解决方案。
如果我需要(再)扩容或配置 DNS,我 应将电话寄到什么地方?
您可以通过提供全程跟踪的速递公司(如 Federal Express,而不通过美国邮政服务)将电话寄至以下地址:

QUALCOMM, Inc
Attn :QIS Phone Center (Bldg R - 212P)
5775 Morehouse Dr.
San Diego, CA 92121

同时,请提供以下信息:
返回地址和电话号码
电话问题/请求性质:再扩容、配置 DNS、电话错误等。
ESN 号
如果激活,使用哪个电话号码和运营商
电话的浏览器是否激活?
如何生成测试签名文件?
要 生成测试签名文件,请使用从 BREW 网站上可以获得的 BREW TestSig Generator。 请采用以下格式输入电话的电子序列号 (ESN): 0xaabcccdd。在 Kyocera 3035 上, ESN 可以通过卸下电池并查看电话后部的标签找到。 ESN 的前缀是“H:”。下面电话的 ESN 是 FEDA6548, 在 TestSig Generator 中必须输入 0xFEDA6548。

在 Sharp Z-800 上,ESN 可以在 Main Menu(主菜单)-> Display(显示)-> Version(版本)中找到。
ISHELL_GetLocation API 存在什么隐私问题?
这由运营商处理此敏感数据的实施策略和用户的个人首选项决定。
BREW 是否支持 Bluetooth?
目前,BREW 不支持 Bluetooth,但我们正在做这方面的工作。
BREW 是否支持电子邮件?
目前 BREW API 在本地不支持因特网或其它电子邮件协议。
BREW 是否在本地支持文本语音转换或语音文本转换?
不,但第三方公司可以开发此功能并留出一个接口,以便其它应用程序使用此功能。
如何能在多个模块间共享资源?
默 认情况下,动态加载的 BREW 模块的文件访问权限局限于应用程序目录及其子目录。 不过,可能会出现模块共享资源或其它类型文件的需求; 在这种情况下,BREW 提供一个共享目录, AEE_SHARED_DIR。实际共享目录路径对于各个设备各有不同。 在 BREW Emulator 上运行时,共享目录可以在 名为“Shared(共享)”的示例目录中找到。

任何模块都可以从共享目录中读取文件。但是,要在共享目录中创建 或写入文件,必须使用 MIF 编辑器修改应用程序的权限级别, 以便提供共享目录的“写”权限 (请参阅 BREW SDK 随附的《BREW MIF 编辑器指南》)。

该目录的全限定路径可以如下所示进行构建:
char sharedBarFile[20];
SPRINTF(sharedBarFile, "%s/%s", AEE_SHARED_DIR, "shared.bar");


例如,在 Kyocera 电话上,前面的代码将用“/brew/sch/shared/shared.bar”填充“sharedBarFile”, 然后可以将其用于从 shared .bar 文件加载资源。 此技术适用于需要在应用程序间共享的任何类型的文件。
删除/禁用应用程序对应用程序数据和首选项有什么影响?
使用 Mobile Shop(移动商店)删除/禁用应用程序:

如果删除应用程序,则应用程序的数据和首选项也一同删除。 应用程序间共享的数据是例外; 该共享数据在删除应用程序时不被删除。有关 共享数据的详细信息,请参阅 FAQ。

版本 1.0.1 在删除首选项方面存在缺陷:删除应用程序时,无法删除使用 ISHELL_SetPrefs() 设置的首选项。 该缺陷已在 BREW 版本 1.1 中修复。

如果禁用应用程序,则不删除应用程序的数据和首选项。

使用 BREW App Loader 删除应用程序:

如果使用 BREW App Loader 删除应用程序,则该应用程序的首选项将保留。
运行应用程序时为何收到“未知错误 (1)”?
一般情况下,.mif 文件中指定的 Class ID 和应用程序代码中的 Class ID 不匹配时会出现该错误。 如果以下条件的值为 false,则会出现该错误(在模拟器和电话上都会):
int AEEClsCreateInstance(AEECLSID ClsId, Ishell* pIShell,
Imodule* po,
void** ppObj) {
**ppObj = NULL;
if(ClsId == AEECLSID_MYAPP) {
//要继续,就必须为 true
// AEECLSID_MYAPP 值包含在 .bid 文件中
}
}


确保 .mif 文件中的 Class ID 与 .bid 文件中的 Class ID 相同。 请注意您可以使用 MIF 编辑器在本地生成 Class ID,用于测试。

试图启动应用程序时为何收到以下错误? 错误:“应用程序名 - 为节约内存, 已卸载该应用程序。必须下载新的版本”。
该消息出现在模拟器上时表示 dll 应用程序丢失。 在模拟器中运行前使用 Visual C++(Build(编译)->Rebuild All(全部重新编译))编译应用程序。

该消息出现在电话上时表示找不到 mod 文件。 如果应用程序是使用 Mobile Shop(移动商店)下载的,则可以选择 Yes(是)重新加载。 如果应用程序是使用 BREW App Loader 下载的,请确保您已经下载了 .mod 文件。
在什么情况下,小程序无法启动(即 ISHELL_CanStartApplet() 返回 False)?
显示重要对话框(如Privacy Alert(隐私警告)对话框)时小程序无法启动。 某些情况取决于 OEM,如进行语音呼叫或数据呼叫时是否启动小程序。 例如,Kyocera 3035 允许小程序在进行语音呼叫时启动,而 Sharp Z-800 不允许。
运行拥有基于使用的许可的应用程序时,为什么在关机后再开机时剩余使用次数会重置为原始值?
许 可信息存储在 MIF 文件中。应用程序调用 ILICENSE_SetUsesRemaining()、ILICENSE_DecrementUsesRemaining 或 ILICENSE_IncrementUsesRemaining() 时,BREW 试图更新 MIF 文件。 由于 BREW SDK 版本 1.0.1 中有缺陷,应用程序必须拥有文件权限级别才能成功更新 MIF 文件。 因此,SDK 1.0.1 的暂时解决办法是包括文件权限级别(通过 MIF 编辑器)。 这已在 SDK 版本 1.1 中修复,即基于使用的应用程序在 1.1 中不必拥有文件权限级别。 有关权限的详细信息, 请参阅此 FAQ。
如何确定我的电话上的软件版本?
在 Kyocera 3035 上:

定位到空闲屏幕
键入 111111,然后按 Select(选择)键
选择 Version(版本)菜单项
查找 SW Version(软件版本)下列出的版本号码。例如: LR2.0.31
在 Sharp Z-800 上:
软件版本可以在 Main Menu(主菜单)-> Display(显示)-> Version(版本)下找到。

如何能将数据从一个应用程序传递到另一个应用程序?
可以通过在共享数据位置创建文件/数据库在应用程序间共享数据。 请参阅 FAQ。
将 数据从一个应用程序传递到另一个应用程序的另一种方法是通过 ISHELL_SendEvent() 或 ISHELL_PostEvent 函数,不过这不是推荐方法。 可以将这两个函数中事件特定的 32 位值 (dwParam) 参数指向 App A 中的数据结构。 App B 接收事件时,应该立即复制数据。 这一点很重要,因为如果 App B 决定为响应事件而自动启动,则会在没有足够 RAM 启动 App B 的情况下停止并卸载 App A,因而使 App A 在 dwParam 中向 App B 提供的指针无效。

例如:

// 应用程序 A

// 要传递到应用程序 B 的数据是一个宽字符串:pMe->myString

ISHELL_SendEvent(pMe->a.m_pIShell, AEECLSID_APP_B, EVT_USER,

MY_USR_EVT,(uint32) pMe->myString);

//应用程序 B

case EVT_USER:
if(wParam == MY_USR_EVT) {
AECHAR* data = WSTRDUP((AECHAR*)dwParam);

// 可以通过调用 ISHELL_StartApplet 启动应用程序 B
... ... ...
... ... ...
FREE(data);
return TRUE;
}

请 注意不保证应用程序能够接收已发送/传递给它的所有事件。 下面举例说明,请考虑可用堆为 60K 的设备。 假定 App A 的大小为 45K,App B 的大小也是 45K。 如果 App A 试图向 App B 发送/传递事件,则由于 RAM 限制不会加载 App B,该事件会被丢弃。

什么是“Preparing Applications(准备应用程序)”?
BREW 内核应用程序的一个函数,是向“应用程序下载服务器 (ADS)” 发送新下载的应用程序和最近删除的应用程序的确认。 例如,通过 MobileShop 删除应用程序时, BREW 保持一个记录,说明需要将删除确认 (ack) 发送到服务器。 BREW 试图在下次连接到 MobileShop 时或下次打开手持设备电源并启动 BREW 时发送 ack 并启动 BREW(在第二种情况下,BREW 启动内核应用程序发送确认)。 BREW 内核应用程序是由 BREW 内部启动 - 用户无法启动内核应用程序。

如果 BREW 内部启动内核应用程序时有应用程序正在运行, 则会暂停该正在运行的应用程序。因此,应用程序只需处理“中止/恢复”事件 (如中止 和恢复应用程序中所述),就可正确处理“Preparing Applications(准备应用程序)”消息。


我的应用程序中需要包含的图像及其大小要求是什么?
所有 BREW 应用程序均要求包含以下三种指定像素尺寸的应用程序图像:

缩略图: 16 x16 像素
图标: 26 x26 像素
图像: 最大大小视不同的设备而有所不同

KWC 3035 和 Sharp Z-800 上的图像大小如下:
QCP3035: 69 x 46 像素或更小
Sharp Z800: 108 x72 像素或更小
LG VX-10: 120 x100 像素或更小

Sharp 1.08 和 LG VX-10 使用缩略图显示应用程序管理器中的应用程序, 在突出显示一个应用程序时该图像就显示在屏幕中央。 Kyocera 3035 和 Sharp 2.01 使用图标显示应用程序管理器中的应用程序。
是否可以使用 BREW API 获取 ESN 或其它唯一设备标识符?
目前没有可以获取设备 ESN 的 BREW API。 可以通过 BREW API 访问的唯一设备标识符是 设备电话号码。请参阅“如何 获取正在运行我的应用程序的设备电话号码?”
对于我的应用程序可以随附的文件数有限制吗?
由于手持设备上的数据结构限制不同,所以建议使用的文件数要尽可能少。 一些手持设备上有 OEM 施加的 最大 BREW 文件数量限制,且有可能在 用完内存之前已无可用文件。

Sharp Z-800 手持设备有一个 BREW 文件限制,要求 所设计的应用程序使用的文件不得超出八 (8) 个。 包括应用程序包中的文件 - mod、bar、sig 和 mif 文件。还包括应用程序使用的模块。该 限制是基于 Z-800 对所有可用于 BREW 应用程序的文件数有 限制(在 2.01 build 上为 93 个文件)的事实形成的, 反之,该限制又规定了手持设备上适用的应用程序总数的上限。

请注意,8 文件限制适用于非运行时应用程序所用的 文件数。您的应用程序可以 在运行时创建其它文件,但必须在应用程序退出时删除它们。

当前的 Kyocera 3035 构件可用于创建更多的 BREW 文件, 范围为 250。正如前所述, 继续限制应用程序使用的文件数是明智之举。

请注意,应用程序中包含的文件多于 8 个时 可能会导致无法通过 True BREW 测试,或导致 运营商拒收您的应用程序。


使用 ISHELL_SetPrefs() 设置的首选项存储在什么地方? 我必须用“可修改”标记为我的应用程序签名吗?
ISHELL_SetPrefs() 将首选项信息存储在您的应用程序的外部 BREW 数据库中。 由于存储应用程序首选项不 修改任何应用程序文件,不必使用“可修改”标记为应用程序签名。
BREW 如何解决为无线手持设备开发应用程序和服务的问题?
BREW 平台是一个无线手持设备的标准应用程序开发环境,手持设备涵盖范围从便宜的大众化电话到多功能高端无线设备。通过该平台,您可以编写能在多种电话上使用的 应用程序,而不要求对每个新的电话型号进行应用程序修改。BREW 解决了当前的编程难题,如不需要电话及其软件的内部知识。
创建 BREW 应用程序自始至终需要什么?
需 要 BREW 软件开发包 (SDK)、Windows® 开发环境和 ARM 编译器(ARM 已创建了专门支持 BREW 的 ARM BREW 编译器,可以向 BREW 开发者提供,售价 1,500 美元。)您还需要为您的应用程序的 TRUE BREW 兼容性测试进行计划。通过测试的应用程序(测试是无线运营商在提供您的应用程序以便通过其网络下载前要求的)将收到 QUALCOMM 的数字签名。手持设备使用此签名进行验证,之后才允许下载或运行应用程序。
如何获得 BREW SDK?
只需单击此处下载即可。
如何通过编写 BREW 应用程序赚钱?
您 的应用程序经过 TRUE BREW 测试后,QUALCOMM 将它与您的条款声明页传递到 BREW 运营商外联网上。一旦您接受与运营商间的条款,运营商将在他们的 BREW 应用程序下载服务器上向他们的用户提供您的应用程序。客户采用您的应用程序时,将根据您与每个运营商达成的条款向您支付酬金。QUALCOMM 正在将中间件与运营商网络链接起来以管理应用程序分发以及对您的酬金支付。您作为 BREW 开发者有权访问的开发者外联网允许您跟踪运营商及最终用户对您的软件的采用情况。
兼容性测试包括哪些内容?由谁进行测试?
由第三方测试试验室来执行兼容性测试。测试的具体内容因应用程序而异,但一般来说,测试将涉及以下内容:
应用程序如何与核心电话功能交互。例如,用户在另一个应用程序中时应用程序在被推入设备时如何处理即时通信或其它系统警告。
内核应用程序的长期稳定性。向事件队列发送大量的事件,以查看应用程序如何处理它们,看看系统的不稳定性(如崩溃)、内存使用(如峰值)等。
重要的应用程序功能。进行测试以确保您宣称的重要应用程序功能在应用程序中实际存在。QUALCOMM 不会从最终用户角度测试该功能的“正确性”。
应用程序使用的每个 BREW 功能领域的一组标准测试。例如,如果应用程序使用 BREW 文件系统调用,则我们将使用标准的文件系统测试套件。同样,如果应用程序使用 BREW 网络调用,则我们将用标准的网络测试套件进行测试。
任何因运营商或生产商而异的外观要求。
与目标设备的兼容性。
与必需附件的交互。
与 QUALCOMM 指定的一小组内核应用程序的互操作性。例如,如果市场上的大多数电话最终使用某浏览器,则我们将测试所提交的应用程序,确保它能与该浏览器较好地互操作。
QUALCOMM 是否会对兼容性测试收费?
是,QUALCOMM 将按成本回收情况收取测试费用。QUALCOMM 将尽可能降低您的这些成本。

通过后会怎样?
您的应用程序现在已经过 TRUE BREW 测试,可以放在运营商的下载服务器上。QUALCOMM 将使用数字签名对您的应用程序签名。运营商下载服务器和客户手持设备将查找该签名以对您的应用程序下载进行验证。

为 BREW 平台开发还需要哪些其他费用?
BREW 软件开发包 (SDK) 可以免费向开发者提供。QUALCOMM 还将免费提供基于 Web 的开发者支持。您将负责获取自己的开发工具,包括 ARM BREW 编译器。
如何将我的应用程序安装到特定手持设备上或让特定运营商采用我的应用程序?
QUALCOMM 将确保运营商和生产商可以访问已经通过兼容性测试并经过 TRUE BREW 测试的所有应用程序。我们鼓励您也将您的应用程序销售给运营商和生产商。
编写 BREW 应用程序需要多长时间?
这 取决于应用程序的复杂性。许多应用程序可以在一到两月内完成。非常简单的应用程序可以用几周时间写完。BREW 使应用程序的编写更加容易,因为它为今天的 Windows、Palm 和其它大众化开发者提供了一个比较熟悉的标准环境。BREW 还解决了困难的集成问题,而过去需要数月实施时间。
BREW 平台支持哪些编程语言?
BREW 相对于开发语言是中立的。本地 C 和 C++ 应用程序的运行最有效。尽管目前在 C++ 的使用方面存在一定局限,但是我们正在努力消除这些局限。如果 Java 虚拟机在设备、浏览器(WAP、CHTML 等)和其它环境下可用,BREW 还将支持 Java 应用程序的集成。
BREW 将与哪些无线网络协同工作?
QUALCOMM 与日本 KDDI、韩国 Korea Telecom Freetel (KTF) 和美国 LG Telecom、LEAP 和 Verizon Wireless、加拿大 Bell Mobility、墨西哥 Pegaso、南美 Movicom and Telesp、新西兰 NZ Telecom 签署了谅解备忘录。有望在近期公布其它运营商合作伙伴。公司正在与全球其它主要 CDMA 运营商进行积极的磋商。QUALCOMM 预计启用 BREW 的服务将在 2001 年年底开始推出,2002 年将推出更多的服务。
如果使用 BREW 有问题,可以到何处求助?
除此 FAQ 和 BREW SDK 中的文档外,您可以在 http://www.qualcomm.com/brew/sch/developer/support/knowledge.html 网站上查找到更多有深度的知识库文章。如果您在 SDK 文档、此 FAQ 或知识库中没有找到查找的答案,且您已注册为 BREW 开发者,则可以给 QUALCOMM 的开发支持组发送电子邮件,地址为:[email protected]

如果我的应用程序已放入运营商目录,该如何监视使用情况?
1. 从具有您的 QUALCOMM Authentic Document ID 的计算机上访问 http://notarization.verisign.com/pta。该服务会根据您的 Authentic Document ID 或 QUALCOMM Digital Notarization Certificate 让您进行自我验证。选择此证书并继续。

你可能感兴趣的:(brew)