OPhone系统之电话功能详解

移动开发
OPhone作为一个嵌入式操作系统,目前主要运行在有电话功能的手持设备上,因此最基本的电话功能——如:拨打、接听电话,接收、发送短信,就成为它最重要的核心功能了。

概述

支持OPhone系统完成基本电话功能的底层硬件基础是:无线通讯模块(例如:GSM/GPRSmodem),而在其之上的抽象——无线接口层(RIL:RadioInterfaceLayer)是本文讨论的相关API的灵魂,它完成了对基本电话功能与Radio硬件之间的抽象,负责提供数据的可靠传输、AT命令发送,以及命令回应(response)的解析等工作。它是通讯网络无关的,共包含两个基本部件:RIL守护进程(RILDaemon)和RIL厂商专用实现(VendorRIL)。前者负责初始化VendorRIL实例,并管理来自应用层API的调用——将其转化为“主动请求命令”分派给VendorRIL实现;而后者是具体无线通讯网络的专用实现,掌管并驱动着无线网络硬件模块的通讯工作,并把“被动请求命令”上报给RIL守护进程,从而达成网络通讯。

本文将从拨打电话、接听电话、发送短消息等多个基本功能出发,向大家介绍相关API的具体使用方法。

电话功能

OPhone系统本身有内置的电话应用(Phone.apk)提供拨打和接听电话的能力,它提供了虚拟数字键盘帮助用户拨号并发起呼叫。当有来电呼入时,它会振铃提示并显示来电信息提示界面,用户通过提示界面上的功能按钮,接听或者拒绝来电。但对于开发者而言,我们真正关心的是如何通过API调用这些基本的电话功能?

首先,让我们一起来看与拨打电话相关的功能吧。拨打电话是用户自主发起的动作,所以是“主动请求命令”,它首先在JavaAPI层通过Socket与RIL守护进程建立连接并发送请求命令,然后命令被转发至VendorRIL实现,并且最终建立通话呼叫的网络链接。这是底层的原理和流程,如何通过API实现呢?#t#

还得先从OPhone系统中的Intent(意图)说起,“意图”被用来描述和表达某个要求或者目地,针对拨打电话的意图,有两个与之相关的Intent常量可以使用:Intent.ACTION_CALL和Intent.ACTION_DIAL,区别在于前者会导航到数字拨号键盘,后者直接进行呼叫,具体请看示例代码:

  1. //取得目标号码     
  2. String number = editText.getText().toString();     
  3. Uri data = Uri.parse("tel:" + number);     
  4. //调用拨打电话功能     
  5. if (v.getId() == R.id.btn_dial) {     
  6.    //进入拨号界面     
  7.    Intent intent = new Intent(Intent.ACTION_DIAL, data);     
  8.    intent.addCategory(Intent.CATEGORY_DEFAULT);     
  9.    startActivity(intent);     
  10. } else if (v.getId() == R.id.btn_call) {     
  11.    //直接进行呼叫     
  12.    Intent intent = new Intent(Intent.ACTION_CALL, data);     
  13.    intent.addCategory(Intent.CATEGORY_DEFAULT);     
  14.    startActivity(intent);     
  15. }    

代码非常简单,关键是使用Intent.ACTION_DIAL或Intent.ACTION_CALL做为Intent的action名称,并把Uri类型的data数据作为目标电话号码附加到Intent中。

除直接拨打电话之外,还有一种需求特别常见——即捕获拨打电话的动作并获取目标电话号码,以便做出进一步的具体处理。要实现该需求还得先了解一下背后的故事:在OPhone系统中有一种被称为广播接收器(BroadcastReceiver)的应用程序类型,它会选择接收某个“频道”上的广播数据并做出相应处理,广播数据的产生源头可以是系统中的任何应用程序。对“拨打电话”而言,广播数据在Intent.ACTION_NEW_OUTGOING_CALL频道上,所以要监听该动作,就需要编写广播接收器,并将其指向以上的特定频道。

请看示例代码:

  1. public class OutgoingCallReceiver extends BroadcastReceiver {     
  2.         // logger name     
  3.         private static final String tag = "OutgoingCallReceiver";     
  4.         @Override    
  5.         public void onReceive(Context context, Intent intent) {     
  6.             // 监听到拨打电话动作,并获取目标电话号码     
  7.            String name = Intent.EXTRA_PHONE_NUMBER;     
  8.            String phoneNumber = intent.getStringExtra(name);     
  9.             Log.d(tag, "Outgoing call -> " + phoneNumber);     
  10.            // TODO:you code....     
  11.         }     
  12.     }    

然后,再将以上广播接收器申明在AndroidManifest.xml文件中:

  1. <receiver android:name="OutgoingCallReceiver">     
  2.     <intent-filter>     
  3.      <action android:name=  
  4. "android.intent.action.NEW_OUTGOING_CALL"/>     
  5.       <category android:name="android.intent.category.DEFAULT"/>     
  6.    </intent-filter>     
  7. </receiver>    

其中action.name指向的正是常量Intent.ACTION_NEW_OUTGOING_CALL的字符串字面值。当再次拨号时,无论使用上文介绍的API或通过内置的电话应用程序,该接收器都会自动捕捉到并且获取目标电话号码。
再进一步考虑,假设需要将某特定号码屏蔽掉——禁止用户拨打该号码,要怎么才能做到呢?其实,只需要在广播接收器的实现代码中,有选择的终止广播数据传播即可。请看示例代码:

  1. //禁止向[139999999]号码拨打电话     
  2.    String target = "139999999";     
  3.    if (phoneNumber.equalsIgnoreCase(target)) {     
  4.       // 终止广播数据的传播     
  5.       abortBroadcast();     
  6.       // 显示提示信息     
  7.       String msg = "号码[" + target + "]被禁止~!";     
  8.       Toast.makeText(context, msg, Toast.LENGTH_LONG).show();     
  9.    }    

需要注意的是,并不是所有广播都可以终止,只有有序广播(Orderedbroadcasts)才能够被终止。什么是有序广播呢?#p#

让我们来回顾一下关于BroadcastReceiver的基础知识:广播被分为两种不同的类型:“普通广播(Normalbroadcasts)”和“有序广播(Orderedbroadcasts)”。前者是完全异步的,所有接收器(逻辑上)都在同一时刻运行,对消息传递的效率而言这是很好的做法,但缺点是:接收器不能返回结果并且无法终止广播数据的传播;然而后者是逐个的执行接收器——系统会按照接收器申明的优先级别(申明在intent-filter元素的android:priority属性中),按顺序逐次执行。OPhone系统定义:使用Context.sendBroadcast方法发起的都是普通广播,而有序广播必须使用另一个方法:Context.sendOrderedBroadcast。幸运的是“向外拨打电话”时系统发出的正是有序广播,因此我们可以轻松地阻止它。

继续深入“接听电话”功能,它是用户被动响应的活动,所以属于“被动请求命令”。来电首先会被VendorRIL实现模块发现,然后上报给RIL守护进程,由守护进程再向更高抽象层次的应用程序报告,最终将依次触发振铃提示、显示来电信息界面等一系列的动作。我们关注的焦点依然是:如何监听到来电并获取电话号码?解决该问题的方法之一是,通过系统服务Context.TELEPHONY_SERVICE注册关注电话状态变化的监听器,从而监测到来电信息。

OPhone系统提供了PhoneStateListener对象做为监听器的抽象,它是用于即时监测:服务状态、信号强度、消息等待指示等各方面有关电话功能状态变化的回调方法机制。想要监测来电呼叫,PhoneStateListener的onCallStateChanged方法是入口点,它把电话呼叫状态分为三种类型:空闲(IDLE)、振铃(RINGING)和摘机(OFFHOOK),其中振铃状态正是来电呼入的标志,因此具体的方法是:重新实现PhoneStateListener对象的onCallStateChanged方法,并关注RINGING状态。请看示例代码:

  1. class MyPhoneStateListener extends PhoneStateListener {     
  2.         public void onCallStateChanged(int state, String incoming) {     
  3.            switch (state) {     
  4.            case TelephonyManager.CALL_STATE_RINGING:     
  5.                // Ringing-振铃,有电话呼入     
  6.                Log.d(tag, "RINGING~");     
  7.                Log.d(tag, "获得来电号码:" + incoming);     
  8.                // TODO:YOU CODE     
  9.                break;     
  10.            case TelephonyManager.CALL_STATE_OFFHOOK:     
  11.                // Offhook-摘机,呼出电话已接通或呼入电话已接起     
  12.                Log.d(tag, "OFFHOOK~");     
  13.                break;     
  14.            case TelephonyManager.CALL_STATE_IDLE:     
  15.                // IDLE-空闲,结束通话状态     
  16.                Log.d(tag, "IDLE~");     
  17.                break;     
  18.            }     
  19.         }     
  20.     }    

然后,再将该监听器对象注册到系统服务TelephonyManager中,以下示例代码演示了注册和解除监听器的方法:

  1. //取得系统服务:TelephonyManager     
  2. String sname = Context.TELEPHONY_SERVICE;     
  3. TelephonyManager tm = (TelephonyManager)   
  4. getSystemService(sname);     
  5.       
  6. //注册监听器     
  7. phoneStateListener = new MyPhoneStateListener();     
  8. tm.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);     
  9. Log.d(tag, "listen ok~!");     
  10.       
  11. // 解除监听器     
  12. tm.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);     
  13. Log.d(tag, "listen cancel~!");   

系统服务TelephonyManager位于android.telephony包下,其listen方法用于注册和解除自定义的监听器对象,注册与解除的区别体现在listen方法的第二个参数——int类型的标志(flag)位上。

遗憾的是,虽然我翻阅了大量文档,但仍未能找到在监听器中拒绝来电的方法——即如何拒接的问题,有兴趣的读者请继续研究。

短消息功能

除电话功能之外,短消息功能也是特别常用的基本电话功能。同样在OPhone系统中内置有短消息应用(Mms.apk),提供了编写、发送、接收和阅读短消息等功能。本文将要讨论的重点是:在API级别使用短消息功能的具体方法。

通过API发送短消息的代理是android.telephony包下的SmsManager类,它提供的getDefault()静态方法可以获得对象实例,sendTextMessage方法用于发送短消息,sendMultipartTextMessage方法用于发送多条短信。具体用法请看示例代码:

  1. // 准备短消息内容及相关信息     
  2. String to = "5554";// 目标号码     
  3. String from = null;// 发送人号码     
  4. String content = "message text content~~";  
  5. // 短消息内容   // 发送后 执行的Intent     
  6. Intent i = new Intent();     
  7. PendingIntent sent = PendingIntent.getBroadcast  
  8. (this, 0, i, 0);     
  9. // 发送给接收人之后(此处应该指:信息报告或回执)   
  10. 执行的Intent     
  11. PendingIntent delivery = PendingIntent.getBroadcast(this, 0, i, 0);     
  12. // 发送短消息     
  13. SmsManager manager = SmsManager.getDefault();     
  14. manager.sendTextMessage(to, from, content, sent, delivery);    

值得注意的是做为参数的PendingIntent对象,可以把它简单的理解成:“即将发生的Intent”,也就是说在短信发送完成后和短信送达后,分别向系统发出广播通知,关注这一事件的应用将监听此广播。当然也可以用PendingIntent.getActivity或者getService来创建PendingIntent对象,从而做出其它形式的响应。

本来对于短消息功能而言,同样会面临要监听“收到”和“发出”短消息事件的需求,然而,实现方法与监听电话功能的方法却完全不同——系统并未提供监听器注册接口和底层广播消息等途径,而是要依赖于“内容提供者(Contentproviders)”类型的应用程序,它具有观察者模式机制,该机制的基本思路是:OPhone系统会把短消息数据(包括发出的和接收到的)保存在一个内容提供者应用程序中(你可以理解成Database或者文件系统的资料库),该程序允许开发者向其注册“观察者对象(ObserverObject)”,以即时了解资料库中数据的变化,因此只要编写自己的观察者对象,并注册到“发出”或“收到”短信所在的资料库中,就可以监听到相关事件了。

简单说一下“内容提供者(Contentproviders)”,它是一种独特的应用程序类型,是保存和读取数据的高级抽象接口,接口实现的背后可以使用数据库、文件,甚至是远程网络来持久保存数据,并且它在一定程度上也呼应着REST架构的思想,例如:使用URI命名标识具体数据;具有增(insert)、删(delete)、改(update)、查(query)4个基本操作方法,等等。在OPhone系统中,用来存储短消息数据的内容提供者程序是:TelephonyProvider。

然而令人遗憾的是,以上方法在当前OPhone版本(OPhoneSDK_1.5.beta)中并不可用。通过编写代码实际测试发现,TelephonyProvider应用并未响应观察者机制,虽然注册新的观察者不会导致错误,但也并未按预想对onChange方法进行回调,初步判断可能是当前的TelephonyProvider应用有特殊的定制和实现。具体原因还有待进一步学习和研究,同时也欢迎对此有兴趣的读者与我一道共同寻找解决方法。

总结

本文介绍了OPhone平台中基本电话功能的基础知识,并通过示例代码演示了在API级别,使用和控制基本电话功能的具体方法和技巧。

责任编辑:chenqingxiang 来源: ophonesdn
相关推荐

2010-07-23 14:51:09

OPhone开发

2022-02-17 20:57:07

OpenHarmon操作系统鸿蒙

2009-03-24 08:33:14

AndroidGoogle移动os

2011-05-05 09:56:01

OPhoneAndroid

2010-09-17 10:18:59

ODTOPhone

2010-03-04 16:08:21

Android系统平台

2022-05-10 11:17:27

电话子系统数据服务模块

2011-03-03 11:30:09

Pureftpd

2009-08-21 10:51:55

ASP.NET Rou解析URL

2010-09-16 13:38:13

OPhone

2010-05-12 14:18:32

思科统一通信系统

2010-07-26 15:12:20

坐标变换

2010-05-19 09:33:27

Linux系统监控工具top

2010-09-27 13:49:06

移动开发OPhoneUPhone

2010-05-19 10:14:41

vmstatLinux系统监控工具

2009-11-25 17:48:18

PHP文件系统相关函数

2018-06-15 15:50:34

技术

2010-05-21 14:00:42

2011-02-21 10:07:46

OPhone 2.5OPhone

2010-07-23 16:08:38

OPhone平台
点赞
收藏

51CTO技术栈公众号