Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2287
  • 博文数量: 2
  • 博客积分: 45
  • 博客等级: 民兵
  • 技术积分: 25
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-23 09:53
文章分类
文章存档

2012年(2)

我的朋友
最近访客

分类: Java

2012-11-21 11:01:21

窗体顶端


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

AD: 

概述

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

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

电话功能

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

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

 

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

//取得目标号码     

String number = editText.getText().toString();     

Uri data = Uri.parse("tel:" + number);     

//调用拨打电话功能     

if (v.getId() == R.id.btn_dial) {     

   //进入拨号界面     

   Intent intent = new Intent(Intent.ACTION_DIAL, data);     

   intent.addCategory(Intent.CATEGORY_DEFAULT);     

   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_DIALIntent.ACTION_CALL做为Intentaction名称,并把Uri类型的data数据作为目标电话号码附加到Intent中。

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

请看示例代码:

16 public class OutgoingCallReceiver extends BroadcastReceiver {     

17         // logger name     

18         private static final String tag = "OutgoingCallReceiver";     

19         @Override    

20         public void onReceive(Context context, Intent intent) {     

21             // 监听到拨打电话动作,并获取目标电话号码     

22            String name = Intent.EXTRA_PHONE_NUMBER;     

23            String phoneNumber = intent.getStringExtra(name);     

24             Log.d(tag, "Outgoing call -> " + phoneNumber);     

25            // TODO:you code....     

26         }     

27     }    

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

28  android:name="OutgoingCallReceiver">     

29          

30       android:name=  

31 "android.intent.action.NEW_OUTGOING_CALL"/>     

32        android:name="android.intent.category.DEFAULT"/>     

33         

34     

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

35 //禁止向[139999999]号码拨打电话     

36    String target = "139999999";     

37    if (phoneNumber.equalsIgnoreCase(target)) {     

38       // 终止广播数据的传播     

39       abortBroadcast();     

40       // 显示提示信息     

41       String msg = "号码[" + target + "]被禁止~!";     

42       Toast.makeText(context, msg, Toast.LENGTH_LONG).show();     

43    }    

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

窗体顶端

窗体底端

 

OPhone系统之电话功能详解(2)

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

AD: 

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

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

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

class MyPhoneStateListener extends PhoneStateListener {     

        public void onCallStateChanged(int state, String incoming) {     

           switch (state) {     

           case TelephonyManager.CALL_STATE_RINGING:     

               // Ringing-振铃,有电话呼入     

               Log.d(tag, "RINGING~");     

               Log.d(tag, "获得来电号码:" + incoming);     

               // TODO:YOU CODE     

               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中,以下示例代码演示了注册和解除监听器的方法:

21 //取得系统服务:TelephonyManager     

22 String sname = Context.TELEPHONY_SERVICE;     

23 TelephonyManager tm = (TelephonyManager)   

24 getSystemService(sname);     

25       

26 //注册监听器     

27 phoneStateListener = new MyPhoneStateListener();     

28 tm.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);     

29 Log.d(tag, "listen ok~!");     

30       

31 // 解除监听器     

32 tm.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);     

33 Log.d(tag, "listen cancel~!");   

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

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

短消息功能

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

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

34 // 准备短消息内容及相关信息     

35 String to = "5554";// 目标号码     

36 String from = null;// 发送人号码     

37 String content = "message text content~~";  

38 // 短消息内容   // 发送后 执行的Intent     

39 Intent i = new Intent();     

40 PendingIntent sent = PendingIntent.getBroadcast  

41 (this, 0, i, 0);     

42 // 发送给接收人之后(此处应该指:信息报告或回执)   

43 执行的Intent     

44 PendingIntent delivery = PendingIntent.getBroadcast(this, 0, i, 0);     

45 // 发送短消息     

46 SmsManager manager = SmsManager.getDefault();     

47 manager.sendTextMessage(to, from, content, sent, delivery);    

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

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

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

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

总结

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

阅读(262) | 评论(0) | 转发(0) |
0

上一篇:android电话流程

下一篇:没有了

给主人留下些什么吧!~~