九、清单保证:安全性和安卓权限

本章将涵盖以下食谱:

  • 使用安卓清单文件设置应用权限
  • 防止设备屏幕变暗
  • 建立安卓定制 URI 方案
  • 预期安卓兼容性过滤
  • 说明要安装到设备 sd 卡的应用
  • 加密本地 SQLite 数据库

简介

安卓有一个基于清单文件声明的非常具体的权限和安全系统,允许或限制应用访问各种设备功能。本章将详细介绍如何让您的 Flash Platform 应用正确识别利用 Android Market 过滤、应用本地应用数据库加密和其他有用的花絮所需的权限!

用安卓清单文件设置应用权限

当用户选择在安卓系统上安装应用时,他们总是会收到一条警告,告知该应用在他们的特定系统中将拥有哪些权限。从互联网访问到完全的地理位置、摄像头或外部存储权限;用户被明确告知应用将在他们的系统上拥有什么权限。如果应用要求的权限似乎超出了需要,用户通常会拒绝安装,并寻找另一个应用来执行他们需要的任务。只要求应用真正需要的权限是非常重要的,否则用户可能会对您和您提供的应用产生怀疑。

怎么做...

我们可以通过三种方式修改Android Manifest文件来设置应用权限,以便用 Adobe AIR 编译我们的应用。

使用 Flash Professional:

在安卓 AIR 项目中,打开属性面板,点击玩家选择:旁边的小扳手图标

Using Flash Professional:

将出现安卓设置的空气对话框窗口。您将看到一个为您的应用启用或禁用的权限列表。只选择你的应用需要的,完成后点击确定

Using Flash Professional:

使用闪存生成器:

  1. 首次在 Flash Builder 中为 Android 项目设置 AIR 时,在项目位置区域定义所有需要的内容,然后点击下一步
  2. You are now in the Mobile Settings area of the New Flex Mobile Project dialog. Click the Permissions tab, making sure that Google Android is the selected platform. You will be presented with a list of permissions to either enable or disable for your application. Check only the ones your application will need and continue along with your project setup:

    Using Flash Builder:

  3. 要在开始开发应用后修改这些权限,只需打开 AIR 描述符文件并进行编辑,详见以下章节。

使用简单的文本编辑器:

  1. 在项目中找到 AIR 描述符文件。它通常被命名为类似{MyProject}-app.xml的东西,因为它位于项目根。
  2. 浏览文件寻找一个名为<android>的节点,在这个节点内会有另一个名为<manifestAdditions>which的节点保存着一个名为<manifest>的子节点。文档的这一部分包含了我们为安卓应用设置权限所需的一切。
  3. 我们需要做的就是要么注释掉,要么删除那些我们的应用不需要的特定权限。例如,这个应用需要互联网、外部存储和摄像头访问。每隔一个权限节点使用<!-- {comment here} -->:

    ```java

    ```

    的标准 XML 注释语法进行注释

它是如何工作的...

您在 AIR 描述符文件中定义的权限将用于创建一个安卓清单文件,该文件将被打包到用于编译项目的工具产生的.apk中。一旦应用安装到用户的设备上,这些权限就会限制和启用该应用,并且还会提醒用户在安装之前该应用可以访问哪些活动和资源。一旦安装到设备上,仅提供应用执行预期任务所需的权限非常重要。

以下是安卓清单文档的可能权限列表:

  • ACCESS_COARSE_LOCATION:允许Geoloctaion类访问 WIFI 和三角定位的蜂窝塔位置数据。
  • ACCESS_FINE_LOCATION:允许Geolocation类使用设备全球定位系统传感器。
  • ACCESS_NETWORK_STATE:允许应用通过NetworkInfo类访问网络状态。
  • ACCESS_WIFI_STATE:允许应用通过NetworkInfo类访问 WIFI 状态。
  • CAMERA:允许应用访问设备摄像头。
  • INTERNET:允许应用访问互联网并执行数据传输请求。
  • READ_PHONE_STATE:当电话正在通话时,允许应用静音。
  • RECORD_AUDIO:允许麦克风访问应用来记录或监控音频数据。
  • WAKE_LOCK:允许应用使用SystemIdleMode类阻止设备进入睡眠状态。(必须与DISABLE_KEYGUARD.)T3 一起使用)
  • DISABLE_KEYGUARD:允许应用使用SystemIdleMode类阻止设备进入睡眠状态。(必须与WAKE_LOCK.)T3 一起使用)
  • WRITE_EXTERNAL_STORAGE:允许应用写入外部存储器。该内存通常存储为设备 SD 卡。

防止设备屏幕变暗

安卓操作系统会变暗,经过一定时间后最终关闭设备屏幕。它这样做是为了保持电池寿命,因为显示器是设备的主要功耗。对于大多数应用,如果用户正在与界面交互,该交互将防止屏幕变暗。但是,如果您的应用不涉及长时间的用户交互,而用户正在显示屏上查看或阅读某些内容,那么防止屏幕变暗是有意义的。

怎么做...

AIR 描述符文件中有两个设置可以更改,以确保屏幕不会变暗。我们还将修改应用的属性,以完成此配方:

  1. 在项目中找到 AIR 描述符文件。它通常被命名为类似{MyProject}-app.xml的东西,因为它位于项目根。
  2. 在文件中浏览一个名为<android>的节点,在这个节点内会有另一个名为<manifestAdditions>的节点,它拥有一个名为<manifest>的子节点。文档的这一部分包含了我们为安卓应用设置权限所需的一切。
  3. 我们需要做的就是确保描述符文件的这一部分中存在以下两个节点。请注意,启用这两个权限是允许应用通过SystemIdleMode类控制系统所必需的。如有必要,取消注释。T1】
  4. 在我们的应用中,我们将导入以下类:

    ```java import flash.desktop.NativeApplication; import flash.desktop.SystemIdleMode; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.text.TextField; import flash.text.TextFormat;

    ```

  5. 声明一对TextFieldTextFormat向用户追踪消息:

    ```java private var traceField:TextField; private var traceFormat:TextFormat;

    ```

  6. 现在,我们将为我们的应用设置系统空闲模式,方法是将SystemIdleMode.KEEP_AWAKE常数分配给NativeApplication.nativeApplication.systemIdleMode属性:

    ```java protected function setIdleMode():void { NativeApplication.nativeApplication.systemIdleMode = SystemIdleMode.KEEP_AWAKE; }

    ```

  7. 此时,我们将继续设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList。这里,我们创建一个方法来为我们执行所有这些操作:

    ```java protected function setupTraceField():void { device screenpreventing, from dimmingtraceFormat = new TextFormat(); traceFormat.bold = true; traceFormat.font = "_sans"; traceFormat.size = 24; traceFormat.align = "left"; traceFormat.color = 0xCCCCCC; traceField = new TextField(); traceField.defaultTextFormat = traceFormat; traceField.selectable = false; traceField.multiline = true; traceField.wordWrap = true; traceField.mouseEnabled = false; traceField.x = 20; traceField.y = 20 traceField.width = stage.stageWidth-40; traceField.height = stage.stageHeight - traceField.y; addChild(traceField); }

    ```

  8. 这里,我们只需将当前分配的系统空闲模式字符串输出到我们的TextField,让用户知道设备不会进入睡眠:

    ```java protected function checkIdleMode():void { traceField.text = "System Idle Mode: " + NativeApplication. nativeApplication.systemIdleMode; }

    ```

  9. When the application is run on a device, the System Idle Mode will be set and the results traced out to our display. The user can leave the device unattended for as long as necessary and the screen will not dim or lock. In the following example, this application was allowed to run for five minutes without user intervention:

    How to do it...

它是如何工作的...

有两件事必须要做,才能让它正确工作,这两件事都是绝对必要的。首先,我们必须确保应用通过安卓清单文件拥有正确的权限。允许 AIR 描述符文件中的WAKE_LOCKDISABLE_KEYGUARD的应用权限将为我们做到这一点。第二部分涉及将NativeApplication.systemIdleMode属性设置为keepAwake。这最好通过使用SystemIdleMode.KEEP_AWAKE常数来实现。确保满足这些条件将使应用能够保持设备显示器点亮,并防止安卓在设备空闲后锁定设备。

另见...

在这个食谱中,我们已经通过一个基本的文本编辑器编辑了空气描述符文件。有关在各种环境中设置这些权限的其他方法,请参考前面的方法。

建立安卓定制 URI 方案

安卓向 AIR 公开了许多有用的 URI 协议,用于地图、短信和电话等标准操作。为我们的应用定义一个定制的 URI 允许它在系统的任何地方被调用:通过网络浏览器,电子邮件,甚至一个本地应用。自定义 URIs 提供了一种调用 AIR 应用的替代方法。

怎么做...

我们将创建一个应用,可以使用自定义 URI 从设备网络浏览器打开。我们通过修改 AIR 描述符文件来定义 URI 意图设置:

  1. 在项目中找到 AIR 描述符文件。它通常被命名为类似{MyProject}-app.xml的东西,因为它位于项目根。
  2. 浏览文件找到一个名为<android>的节点;在这个节点内还有另一个名为<manifestAdditions>的节点,它拥有一个名为<manifest>的子节点。文档的这一部分包含了我们为安卓应用设置权限所需的一切。
  3. 我们现在将突出显示的<intent-filter>节点添加到描述符文件中。定义我们 URI 的意图部分是<data android:scheme="fvm"/>。这将使我们的应用能够使用fvm:// URI。注意本例使用"fvm";当基于这样一个例子创作一个应用时,我们可以自由地将这个值更改为任何适合特定应用的值:

    ```java

    ```

  4. 在我们的应用中,我们将导入以下类:

    ```java import flash.desktop.NativeApplication; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.InvokeEvent; import flash.text.TextField; import flash.text.TextFormat;

    ```

  5. 声明一对TextFieldTextFormat向用户追踪消息:

    ```java private var traceField:TextField; private var traceFormat:TextFormat;

    ```

  6. 此时,我们将继续设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList。这里,我们创建一个方法来为我们执行所有这些操作:

    ```java protected function setupTraceField():void { traceFormat = new TextFormat(); traceFormat.bold = true; traceFormat.font = "_sans"; traceFormat.size = 24; traceFormat.align = "left"; traceFormat.color = 0xCCCCCC; traceField = new TextField(); traceField.defaultTextFormat = traceFormat; traceField.selectable = false; traceField.multiline = true; traceField.wordWrap = true; traceField.mouseEnabled = false; traceField.x = 20; traceField.y = 40; traceField.width = stage.stageWidth-40; traceField.height =stage.stageHeight - traceField.y; addChild(traceField); }

    ```

  7. 注册 InvokeEvent 类型的事件侦听器。对 NativeApplication 调用。这将检测由用户使用我们定义的 URI 发起的任何应用调用事件:

    ```java protected function registerListeners():void { NativeApplication.nativeApplication. addEventListener(InvokeEvent.INVOKE, onInvoke); }

    ```

  8. 当从我们的 URI 打开申请时,将处理以下方法。我们可以从调用事件中收集一定数量的信息,例如reason属性。该属性的值为"login""standard"。如果应用在系统登录时自动启动,该值将显示为"login"。在 URI 祈祷的情况下,它将读作"standard"。我们还可以访问currentDirectory。该应用可能是从文件系统中调用的,或者是通过 URI 访问的任何arguments。请注意,在从网络浏览器调用 URI 的情况下,正如我们在这里看到的那样,arguments属性将只包含所选链接的完整网址。这是一种在应用启动时将数据传递给应用的方式。

    ```java protected function onInvoke(e:InvokeEvent):void { traceField.text = ""; traceField.text = "Invoke Reason: " + e.reason + "\n"; traceField.appendText("Directory URL: " + e.currentDirectory. url + "\n\n"); var args:Array = e.arguments; if (arguments.length > 0) { traceField.appendText("Message: " + args.toString() + "\n"); } }

    ```

  9. For this example, let us set up a simple web page which includes a link with our defined fvm:// URI:<a href="fvm://arg1=Hello&arg2=AIRAndroid">Open AIR Android App!</a>. If a user has the application already installed and clicks this link, the application should open as our URI intent is registered on the device:

    How to do it...

  10. Once the user clicks upon the link which uses our defined URI, the AIR application will open and detect an InvokeEvent, which displays the following information upon the device display. We can see here that the directory URL is empty, as the application was not invoked from within the device file system:

    How to do it...

它是如何工作的...

当我们在应用描述符文件中定义 URI 意图时,它会和我们的应用一起编译到 Android 清单文件中。在设备上安装此应用会通知操作系统我们定义的 URI 意图。这使得操作系统知道那个特定的 URI,并指示它在遇到那个 URI 时打开应用。我们可以将 URI 放置在许多不同的位置,包括系统上的原生安卓应用。这允许本地应用为安卓应用打开 AIR。在前面的例子中,我们将 URI 嵌入到 HTML 中,并使用安卓网络浏览器打开我们的应用。

另见...

更多关于在安卓 AIR 中使用 URI 协议的信息,请浏览第 7 章本地交互:StageWebView 和 URI 处理器。

预测安卓兼容性过滤

根据特定应用中使用的应用编程接口,一些安卓设备可能无法提供对预期传感器或硬件挂钩的访问。如果一个用户下载了一个不按预期运行的应用,这个用户会感到沮丧,很可能会给我们一个很差的评价,甚至可能是一个令人讨厌的评论。幸运的是,安卓市场可以代表我们进行一些过滤,以确保只有支持我们应用的设备才能选择下载和安装它。

怎么做...

修改安卓清单文件,指定我们的应用需要哪些特定功能:

  1. 在项目中找到 AIR 描述符文件。它通常被命名为类似{MyProject}-app.xml的东西,因为它位于项目根。
  2. 浏览文件找到一个名为<android>的节点;在这个节点内还有另一个名为<manifestAdditions>的节点,它拥有一个名为<manifest>的子节点。文档的这一部分将包含我们需要为我们的安卓应用声明兼容性的所有内容。
  3. 我们将根据我们的需求添加某些标签。请参见以下信息布局,以确定应该在清单节点中为特定功能依赖项添加什么。设置android:required="false"使功能可选。

使用安卓相机功能时:

<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<uses-feature android:name="android.hardware.camera.flash" android:required="false"/>

使用安卓麦克风功能时:

<uses-feature android:name="android.hardware.microphone" android:required="false"/>

使用地理位置传感器时:

<uses-feature android:name="android.hardware.location" android:required="false"/>
<uses-feature android:name="android.hardware.location.network" android:required="false"/>
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>

使用加速计传感器时:

<uses-feature android:name="android.hardware.accelerometer" android:required="false"/>

它是如何工作的...

通过指定摄像头和麦克风的某些必需或可选功能,我们可以确保只有设备满足这些特定要求的用户才能选择下载和安装我们的应用。我们通过增加我们的 AIR 描述文件来修改安卓清单文件,从而使这些规范为人所知,如本食谱所示。用这些修改编译我们的应用将确保这些规范与我们的.APK一起编码,并在我们的应用发布后通过安卓市场公开。

另见...

有关在安卓 AIR 中使用摄像头和麦克风的更多信息,请查看第 4 章视频和音频输入:摄像头和麦克风访问。

指示将应用安装到设备 sd 卡

通过稍微修改我们的 AIR 应用描述符文件中的安卓清单指令,我们可以通知设备操作系统,如果可能的话,我们的应用应该安装在外部 SD 卡上,而不是内部存储上。这将有助于为操作系统和相关文件保留内部设备存储。

怎么做...

修改安卓清单文件以确定安装位置选项:

  1. 在项目中找到 AIR 描述符文件。它通常被命名为位于项目根的类似{MyProject}-app.xmland的东西。
  2. 浏览文件找到一个名为<android>的节点;在这个节点内还有另一个名为<manifestAdditions>的节点,它拥有一个名为<manifest>的子节点。
  3. 我们将把installLocation属性添加到我们的<manifest>节点。设置应用安装由安卓决定:

    ```java

    ```

  4. To set the application to prefer the device SD card:

    ```java

    ```

    注意

    不能保证设置installLocation="preferExternal会将应用实际安装到设备 SD 卡上。

如果允许,用户还可以通过以下步骤移动应用:

  1. 首先,导航到安装了我们的 AIR 应用的设备上的应用管理屏幕。这个屏幕在大多数安卓设备上的位置是设置|应用|管理应用。现在选择您在此屏幕上创建的 AIR 应用。
  2. To move the application to the device SD card, simply click the button labeled Move to SD card:

    How to do it...

它是如何工作的...

允许用户在一定程度上选择安装应用的位置是一个好主意。在安卓系统上,只有两种选择:设备存储区或外置 SD 卡。考虑到大多数设备在 SD 卡上的存储比内部存储多得多;通过在 AIR 描述符文件内的我们的清单节点上设置android:installLocation="preferExternal",可能最好选择 SD 卡。虽然不能保证安卓在安装我们的应用时会使用外部 SD 卡,但这至少会让系统知道位置是首选。安卓能否将应用安装到外部存储设备上,主要与操作系统版本有关。一般来说,如果设备可以为安卓运行时安装和运行 AIR,它应该有能力做到这一点。

正如我们之前看到的,用户总是可以将应用从内部存储移动到外部存储,如果他们愿意,还可以再移动回来。同样值得注意的是:即使应用安装在设备 SD 卡上,应用存储目录、本地共享对象和任何临时文件仍会写入内部存储。如果我们打算用我们的应用保存大量数据,那么我们将使用File.documents目录或File.user目录将这些数据存储到外部 SD 卡上。

另见...

有关使用本地文件系统的更多信息,请查看第 8 章丰富的访问:文件系统和本地数据库。

加密本地 SQLite 数据库

通常,本地 SQLite 数据库不需要任何安全性或加密。但是,如果我们的应用包含存储在本地应用数据库文件中的敏感数据,我们希望确保入侵者或窃贼无法访问这些信息。令人欣慰的是,我们可以对安卓 AIR 上可用的数据库进行加密,以确保即使用户的设备丢失或被盗,他们的私人信息仍然安全。

做好准备...

为了正确加密数据库文件,我们需要使用加密库。在本例中,我们将使用在http://code.google.com/p/as3crypto/提供的 as3crypto 包。下载.SWC跟随这个例子。

我们需要在我们的项目中提供.SWC。根据所使用的工具,执行此操作的程序会有所不同。

将. SWC 包纳入 Flash Builder 项目的说明

  1. 在您的项目中,选择文件菜单并选择属性
  2. 在左栏中,单击动作脚本构建路径并选择库路径选项卡。在该屏幕中找到标有添加 SWC 的按钮并点击它。
  3. 将出现一个对话框。选择浏览至 SWC 选项,找到包含我们的加密库的.SWC,点击确定
  4. The encryption library will now appear within the Build path libraries section of this screen. Verify that this is correct and exit out of the Properties window. The encryption library is now ready to be used within our mobile Android project.

    Instructions to include a .SWC package into a Flash Builder project

将. SWC 软件包纳入 Flash Professional 项目的说明

  1. Within your Flash project, navigate to the Properties panel and click the little wrench icon next to the Script selection box:

    Instructions to include a .SWC package into a Flash Professional project

  2. This will open the Advanced ActionScript 3.0 Settings dialog window. Choose the Library path tab. Locate the Browse to SWC file icon within this screen and click it. It appears as a white and red box and is the only icon which is not grayscale upon this screen:

    Instructions to include a .SWC package into a Flash Professional project

  3. 将出现一个文件浏览对话框窗口。找到包含我们的加密库的.SWC,点击确定

  4. The encryption library will now appear within the Library path section of this screen. Verify that this is correct and exit out of the Advanced ActionScript 3.0 Settings window. The encryption library is now ready to be used within our mobile Android project:

    Instructions to include a .SWC package into a Flash Professional project

怎么做...

为了加密应用数据库,我们将声明一个密码,并使用外部加密库对其进行加密。这将在创建和打开我们的数据库连接时使用:

  1. 在我们的应用中,我们将导入以下类。确保导入MD5类或适当密钥加密的等效类:

    ```java import com.hurlant.crypto.hash.MD5; import flash.data.SQLConnection; import flash.data.SQLMode; import flash.data.SQLStatement; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.SQLEvent; import flash.filesystem.File; import flash.text.TextField; import flash.text.TextFormat; import flash.utils.ByteArray;

    ```

  2. 我们现在必须声明一些在这个应用中使用的对象。一个String常量将保存我们的纯文本密码,以便以后加密。通常,这将由用户提供,并且为了简单起见,在这里进行了硬编码。我们将需要一个SQLConnection来创建或打开我们的数据库文件,以及一组ByteArray对象和一个MD5对象来执行实际的加密。最后,我们声明一对TextFieldTextFormat来追踪给用户的消息:

    ```java private const pass:String = "AIR@ndr0idIsKo0l"; private var sqlConnection:SQLConnection; private var encryptionPass:ByteArray; private var encryptionKey:ByteArray; private var md5:MD5; private var traceField:TextField; private var traceFormat:TextFormat;

    ```

  3. 此时,我们将继续设置我们的TextField,应用一个TextFormat,并将其添加到DisplayList进行文本输出。这里,我们创建一个方法来为我们执行所有这些操作:

    ```java protected function setupTraceField():void { traceFormat = new TextFormat(); traceFormat.bold = true; traceFormat.font = "_sans"; traceFormat.size = 24; traceFormat.align = "left"; traceFormat.color = 0xCCCCCC; traceField = new TextField(); traceField.defaultTextFormat = traceFormat; traceField.selectable = false; traceField.multiline = true; traceField.wordWrap = true; traceField.mouseEnabled = false; traceField.x = 20; traceField.y = 40; traceField.width = stage.stageWidth-40; traceField.height =stage.stageHeight - traceField.y; addChild(traceField); }

    ```

  4. 为了对我们的数据库进行加密,我们将首先实例化一个ByteArray并调用writeUTFBytes()方法,传入我们预定义的密码常量。这将把我们的String写入字节流。

  5. 现在,实例化一个新的MD5对象和另一个ByteArray,将ByteArray分配给MD5.hash()方法的结果,传入保存密码字节的先前的ByteArray
  6. 实例化一个SQLConnection并注册一个SQLEvent.OPEN类型的事件监听器。一旦成功创建或打开数据库,这将触发一个事件。
  7. 最后,调用SQLConnection.open()方法,将数据库路径作为File对象传递给SQLMode.CREATE的打开模式常量,一个自动压缩的Boolean,默认页面大小为 1024,最重要的是对于这个例子,我们的 MD5 加密的ByteArray:

    ```java protected function encryptDB():void { encryptionPass = new ByteArray(); encryptionPass.writeUTFBytes(pass); md5 = new MD5(); encryptionKey = new ByteArray(); encryptionKey = md5.hash(encryptionPass); sqlConnection = new SQLConnection(); sqlConnection.addEventListener(SQLEvent.OPEN, dbOpened); sqlConnection.open(File.applicationStorageDirectory. resolvePath("encrypted.db"), SQLMode.CREATE, false, 1024, encryptionKey); }

    ```

  8. 只要在有效加密的情况下成功创建(或打开)数据库,就会触发以下方法,向我们的显示器输出关于加密数据库的信息:

    ```java protected function dbOpened(e:SQLEvent):void { traceField.appendText("Encrypted DB Created!\n\n"); traceField.appendText("Pass: " + pass + "\n\n"); traceField.appendText("Key: " + encryptionKey.toString()); }

    ```

  9. When the application is run on our Android device, it will appear as follows. As the key is a truly MD5-encrypted ByteArray, it appears as garbled characters in the TextField, for it is no longer a plain text String:

    How to do it...

它是如何工作的...

如果应用需要对数据库进行加密,则必须在创建数据库时应用加密密钥。实现SQLConnection.open()SQLConnection.openAsync()方法需要我们传入一个使用as3Crypto或类似加密库创建的加密ByteArray密钥。如果我们需要修改加密密钥,我们可以使用SQLConnection.reencrypt()来修改,以与本食谱中演示的相同方式生成密钥。请注意,有效的加密密钥长度必须为 16 个字节。

另见...

更多关于在安卓 AIR 中使用本地数据库的信息,请看第 8 章丰富的访问:文件系统和本地数据库。