Android Mms专题之:Mms源码结构
华为彩信中心原理
取消息
BTS
BSC
BSC MSC/VLR/HLR
BTS
提纲
MMS 原理介绍 华为MMS体系结构、接口及
组网
MMS
业务流程介绍
infoX MMSC的接口(4-1)
序号 接口名称
1 MM1
连接实体
MMSC-WAP GW
说明
3GPP和OMA有很明确的定义, infoX MMSC是查考了两种协议来 实现的,可以兼容支持,其中阅读 报告就是很好的例子。 3GPP等国际规范没有明确定义, 各个厂家都是私有协议。infoX MMSC将MM2接口扩充为软件总线 的接口和功能。
2
MM2
Relay - Server
3
MM3
基于SMTP的接口。3GPP等国际规 MMSC-E-mail Server 范有明确定义,对于E-mail Server 来讲,MMSC就是一个邮件服务器。 用于MMSC之间的互连,大多数厂 家都遵循“归属地服务”的原则, 在不同网络和不同运营商互连上, 建议使用MMSIG实现,MMSCIG左 右两边都是MM4接口。
消息是否到接受 否 方消息中心 消息是否直接发 是 送到手机
相同点:基于存储转发机制;消息编辑、发送、接收等操作方式基本相同
MMS支持的媒体类型
文本:internet大量字符集
图像:JPEG, GIF87a, GIF89a, WBMP,JPEG2000
音频: AMR,MP3,MIDI,WAV
Externel Servers:外部消息系统,如Email、Fax、UM等。
VAS:增值服务系统,由第三方SP提供,供终端用户定制。 G-SCP:网关SCP。MMSC通过G-SCP跟移动智能网系统交互,完成对预付费 MMS用户的支持。
MMS 源码目录结构(android)
MMS 源码目录结构首先应该从AndroidManifest.xml 文件开始,该文件是Android应用(APK)的打包清单,其中提供了关于这个应用程序的基本信息,如名称(application/@label),图标( application/@icon ),等常量信息,但该文件最重要的功能仍然是:向Android系统声明应用程序所包含的组件,包括Activity、Service、Receiver和ContentProvider,另外还会请求系统授予相关权限。
<application>是重要的标记,通常我们都是在此设置应用程序的名称和图标,而在Messaging这个应用中,它扩展了默认的android.app.Application类,在com.android.mms.MmsApp类中覆写了onCreate()方法进行系统初始化、覆写onTerminate() 方法做相关清理工作。
最重要的是<application>标记设置了(任务吸引力) taskAffinity =“android.task.mms”、allowTaskReparenting ="true" 这是两个与Task有关的属性,那么何为任务(Task)呢?A pplication就是一个.apk文件,由若干个组件(可能会包括Activity、Service、Receiver 或者ContentProvider )构成,它是一个物理上的独立存在。
而Task则是指用户借助Application提供的功能完成某件事情,例如:发送一条信息给老朋友。
通常一个Task操作所涉及的内容不会超过A pplication 边界,但在Android中却不是这样,Android允许一个T ask操作横跨多个Apllication。
还以向老朋友发送信息为例,在该任务中,至少需要一个用来输入目标电话号码和消息内容的用户界面——即Android中的Activity组件(这是Messaging应用中的内容),如果进一步要求目标号码是从联系人列表(或通话列表)中选择,而非手工输入,那么就涉及到跨Apllication调用‘联系人应用’中的列表组件了(也是一个Activity )。
MMS协议解析
MMS协议解析MMS(Multimedia Messaging Service)就是我们俗称的彩信,它能提供比SMS(短信)更加丰富的信息,能提供包括图片、视频、声音、各种格式的文字等容。
目前的应用主要有以下几个方面:▪便利性实用性娱乐性互动性个人化▪端到端通信自拍照片自编贺卡/图片带有图片照片和语音片断等表达感情容的增强型聊天电子附件在移动终端上的增强型应用▪娱乐手机铃声/屏保下载节日贺卡明星贺卡多媒体交友利用手机察看自己心仪的对象并通过信息向她表达心意原创基地:利用多媒体短信传播自己的原创动漫和音乐▪新闻/金融服务体育比赛集锦和得分镜头视频剪辑通过移动终端获取多媒体新闻和金融信息分析等▪其他定位业务移动广告一、数据流程MMS系统大致网络拓扑图如下:先介绍一下网络中的节点设备:MMS Client:MMS客户端,就是平常用的手机等移动设备;MMS Proxy Relay:MMS代理中继,用于转发MMS;MMS Server:MMS服务器,用于存储MMS,一般与MMS代理集成在一台设备上;Legacy Wireless Messaging Systems:传统的无线消息系统,比如SMS系统;Email Server:服务器,Ineternet上的服务器,比如139服务器;Other MMS Systems:其他地方或其他运营商的MMS系统;MMS承载于WAP之上,在WAP1.x系统里采用WSP协议,在WAP2.0系统里采用HTTP协议,如下图所示:详细网络图如下:MMS 的发送接收实体包括如下几种方式:SGSNGGSNMSCISMGSMSGWAP GWMMS 终端1MMS 终端2SPE-mail ServerMMSCMMSCBSBS核心网IP 网Internet1、终端<——>同一系统终端2、终端<——>不同系统终端3、终端<——>Email server4、终端<——>SP我们以第一种情况进行MMS收发流程说明,大致有以下几步:1、发送终端发送MMS到MMS代理;2、MMS代理发送通知到接收终端;3、接收终端发送提取MMS命令到MMS代理;4、MMS代理返回MMS到接收终端;5、MMS代理发送回执给发送终端,表示对方已收到MMS;其中第2步的通知是通过WAP PUSH实现的,目前中国移动实现的方式是通过SMS(短信)通知对方。
Android源码分析一Android系统架构
Android源码分析⼀Android系统架构⼀ Android系统架构1. Linux内核层(Linux Kernel):Android系统基于Linux2.6内核,这⼀层为Android设备各种硬件提供了底层驱动,如显⽰驱动、⾳频驱动、照相机驱动、蓝⽛驱动、Wi-Fi驱动、电源管理等;2. 硬件抽象层(android hardware abstraction layer):安卓驱动硬件的⽅式与Linux不尽相同。
传统Linux的驱动完全存活于内核空间。
Android则在内核外部增加了硬件抽象层(HAL, Hardware Abstraction Layer),把⼀部分驱动功能放到HAL层中。
安卓为什么费尽⿇烦增加⼀个HAL呢?为了保护源代码。
Linux内核采⽤了GPL协议,所以硬件⽣产商想要⽀持Linux系统,必须遵照GPL协议公开硬件驱动的源代码。
但这些源代码中包含有许多硬件的设计信息,牵涉到硬件⽣产商的核⼼利益。
⽽增加了HAL层之后,硬件⼚商就不需要开放所有的驱动代码了。
3. 系统运⾏库层(libraries):这⼀层通过⼀些C/C++库(so库)来为Android系统提供了主要的特性⽀持。
如SQLite库提供了数据库⽀持,OpenGL ES库提供了3D绘图⽀持,Webkit库提供了浏览器内核⽀持等;4. 应⽤框架层(application framework):这⼀层主要提供构建应⽤程序时可能⽤到的各种API,Android⾃带的⼀些核⼼应⽤就是使⽤这些API完成的,开发者也可通过使⽤API来构建⾃⼰的应⽤程序;a) Activity Manager(活动管理器)管理各个应⽤程序⽣命周期以及通常的导航回退功能b) Window Manager(窗⼝管理器)管理所有的窗⼝程序c) Content Provider(内容提供器)使得不同应⽤程序之间存取或者分享数据d) View System(视图系统)构建应⽤程序的基本组件e) Notification Manager(通告管理器)使得应⽤程序可以在状态栏中显⽰⾃定义的提⽰信息f) Package Manager(包管理器)Android系统内的程序管理g)Telephony Manager(电话管理器)管理所有的移动设备功能h)Resource Manager(资源管理器)提供应⽤程序使⽤的各种⾮代码资源,如本地化字符串、图⽚、布局⽂件、颜⾊⽂件等i)Location Manager(位置管理器)提供位置服务j)XMPP Service(XMPP服务)提供Google Talk服务1. 应⽤层(applications):这⼀层主要⽤于⼿机应⽤的安装,如系统⾃带联系⼈、短信等程序,或是第三⽅应⽤程序 6.Android运⾏时库(Android Runtime)ART以上为五层五区,还有⼀个区域是存在于libraries层的Android运⾏时库(Android Runtime)ART,它主要提供⼀些核⼼库,能够允许开发者使⽤Java语⾔来编写Android应⽤。
Android telephony MMS 学习笔记
本文主要从以下几个方面来学习MMS在android系统中的处理:MMS初始化、MMS发送、MMS接收(包括push MMS接收和从MMSC中提取MMS内容)、MMS存储/删除等数据操作。
Android MMS基本知识点一、MMS概述MMS是在短消息业务基础上发展起来的一种消息业务,它可以用于传送文字、图片、动画、音频和视频等多媒体信息。
MMS采用"存储转发"的技术,用户创建的信息能够自动、快速的在手机和手机之间传送;信息的传送仍然按接收方手机号码进行定位;当接收方关机或暂时不在服务区的情况下,信息将存储在多媒体消息中心(MMSC),直到能够正确送达为止。
MMS消息服务要求一个WAP网关,一个数据传输网如电路交换网、GPRS或WCDMA网络,和一个多媒体消息中心(MMSC)。
在目前,MMS业务主要是以WAP作承载,以短消息作提示通知,由MMS手机自动到多媒体消息中心(MMSC)去提取来实现的。
在android中,MMS主要的处理都在app层,在framework层中主要涉及MMS pdu包的解析处理和发送和接受MMS时的网络处理。
MMS会使用telephony framework部分的类,详细信息请参考《Android_telephony_framework》系列文档。
二、MMS相关Service1. TransactionService主要是通过相应的transaction来处理MMS的发送、接收等请求。
TransactionService包含一个ServiceHandler handler内部类,用来处理MMS相应事件。
三、MMS相关receiver1. PushReceiver接受Intent.WAP_PUSH_RECEIVED_ACTION,启动TransactionService来传递对应的push数据。
2. MmsSystemEventReceiver接受Mms.Intents.CONTENT_CHANGED_ACTION、TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED和intent.ACTION_BOOT_COMPLETED。
移动增值应用开发技术导论第四章彩信MMS篇
• (5)MMS增值应用平台(MMS VAS Applications):基于多媒体消息平台的增值应 用平台;多媒体消息中心应提供开放的、标准 的API接口,支持增值应用开发;
• 不具备彩信功能的用户提供SMS短信通知;
• 提供彩信手机和电子邮件之间传递MMS彩信信 息的能力;
• 提供多媒体呼叫应答语音服务
B.终端-邮箱业务流程 ① 彩信手机发送给邮箱 ② 梦网邮箱发送给彩信手机
C.终端-应用业务流程 ① 登陆网站、发送短信、登陆WAP或者直
接发送彩信等方式点播或定制。
② 用户可以给自己,也可以给别人点播定 制彩信应用。
③ 如果系统判断接受方是非彩信用户,则 系统拒绝发送此条彩信,短信通知。
SP彩信业务现状
• (8)MM8:MMS服务中心和计费系统间的 参考点。
MMS增值应用平台 MM7
MMS用户代理A MM1
HLR MM5
MMS用户数据库 MM6
MM8
计费系统
MMS中继服务器 /多媒体消息中心
Relay<-> Server (MM2)
MMSC MM3
外部应用服务器
eg.E- Mail. Voice Mai.lFox
MMSE(多媒体业务环境)
MMS VAS应用 HTTP
服务器
MM7
计费系统 FTP
MM8
E-mail服务器
SMTP MM3
MMSC NAS MM
存储器
MMS服务器
SMSC
非MMS终端 SMPP 支持系统
用户数据库 MM6
No.7 信令网络
外部SMSC 外部
用户数据库
WAP网关
HTTP MM1
【Android】Android彩信发送的两种方式+源代码
【Android 】Android 彩信发送的两种⽅式+源代码Android 彩信发送的两种⽅式第⼀种:直接调⽤彩信发送接⼝ 实现代码如下, 看到彩信发送的代码,跟短信发送的代码有很⼤的不同,彩信发送不同于短信发送,调⽤系统的彩信发送会出现发送界⾯。
有朋友就要问了,这样不适合我的需求,我需要实现⾃定义彩信发送,并且不调⽤系统彩信。
第⼆种⽅法将满⾜我们的需求第⼆种:⾃定义彩信发送 ⾃定义彩信发送,⽆需进⼊彩信发送界⾯,需要调⽤系统源码 PDU 实现。
⾸先给出发送代码APNManager.getSimMNC ⽤来设置 彩信Url 和代理端⼝MMSSender.sendMMS 实现彩信的发送APNManager 类源代码Intent intent = new Intent(Intent.ACTION_SEND);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.putExtra(Intent.EXTRA_STREAM, Uri.parse(url));// uri 为你的附件的uriintent.putExtra("subject", "it's subject"); //彩信的主题intent.putExtra("address", "10086"); //彩信发送⽬的号码intent.putExtra("sms_body", "it's content"); //彩信中⽂字内容intent.putExtra(Intent.EXTRA_TEXT, "it's EXTRA_TEXT");intent.setType("image/*");// 彩信附件类型intent.setClassName("com.android.mms","poseMessageActivity");startActivity(intent);//彩信发送函数public static void sendMMS(final Context context, String number,String subject, String text, String imagePath, String audioPath) {final MMSInfo mmsInfo = new MMSInfo(context, number, subject, text,imagePath, audioPath);final List<String> list = APNManager.getSimMNC (context);new Thread() {@Overridepublic void run() {try {byte [] res = MMSSender.sendMMS (context, list,mmsInfo.getMMSBytes());} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}};}.start();}View Codepackage com.rayray.util;import java.util.ArrayList;import java.util.List;import android.content.ContentResolver;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import .Uri;import android.telephony.TelephonyManager;import android.text.TextUtils;import android.util.Log;public class APNManager {// 电信彩信中⼼url ,代理,端⼝public static String mmscUrl_ct = "http://mmsc.vnet.mobi";public static String mmsProxy_ct = "10.0.0.200";// 移动彩信中⼼url ,代理,端⼝public static String mmscUrl_cm = "";public static String mmsProxy_cm = "10.0.0.172";// 联通彩信中⼼url ,代理,端⼝public static String mmscUrl_uni = "http://mmsc.vnet.mobi";public static String mmsProxy_uni = "10.0.0.172";private static String TAG = "APNManager";private static final Uri APN_TABLE_URI = Uri.parse("content://telephony/carriers");// 所有的APN 配配置信息位置private static final Uri PREFERRED_APN_URI = Uri.parse("content://telephony/carriers/preferapn");// 当前的APNprivate static String[] projection = { "_id", "apn", "type", "current","proxy", "port" };private static String APN_NET_ID = null ;private static String APN_WAP_ID = null ;public static List<String> getSimMNC(Context context) {TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);String imsi = telManager.getSubscriberId();if (imsi != null) {ArrayList<String> list = new ArrayList<String>();if (imsi.startsWith("46000") || imsi.startsWith("46002")) {// 因为移动⽹络编号46000下的IMSI已经⽤完,所以虚拟了⼀个46002编号,134/159号段使⽤了此编号 // 中国移动list.add(mmscUrl_cm);list.add(mmsProxy_cm);} else if (imsi.startsWith("46001")) {// 中国联通list.add(mmscUrl_uni);list.add(mmsProxy_uni);} else if (imsi.startsWith("46003")) {// 中国电信list.add(mmscUrl_ct);list.add(mmsProxy_ct);}shouldChangeApn(context);return list;}return null;}private static boolean shouldChangeApn(final Context context) {final String wapId = getWapApnId(context);String apnId = getCurApnId(context);// 若当前apn不是wap,则切换⾄wapif (!wapId.equals(apnId)) {APN_NET_ID = apnId;setApn(context, wapId);// 切换apn需要⼀定时间,先让等待2秒try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}return true;}return false;}public static boolean shouldChangeApnBack(final Context context) {// 彩信发送完毕后检查是否需要把接⼊点切换回来if (null != APN_NET_ID) {setApn(context, APN_NET_ID);return true;}return false;}// 切换成NETAPNpublic static boolean ChangeNetApn(final Context context) {final String wapId = getWapApnId(context);String apnId = getCurApnId(context);// 若当前apn是wap,则切换⾄netif (wapId.equals(apnId)) {APN_NET_ID = getNetApnId(context);setApn(context, APN_NET_ID);// 切换apn需要⼀定时间,先让等待⼏秒,与机⼦性能有关try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}Log.d("xml", "setApn");return true;}return true;}// 切换成WAPAPNpublic static boolean ChangeWapApn(final Context context) {final String netId = getWapApnId(context);String apnId = getCurApnId(context);// 若当前apn是net,则切换⾄wapif (netId.equals(apnId)) {APN_WAP_ID = getNetApnId(context);setApn(context, APN_WAP_ID);// 切换apn需要⼀定时间,先让等待⼏秒,与机⼦性能有关try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}Log.d("xml", "setApn");return true;}return true;}// 获取当前APNpublic static String getCurApnId(Context context) {ContentResolver resoler = context.getContentResolver();// String[] projection = new String[] { "_id" };Cursor cur = resoler.query(PREFERRED_APN_URI, projection, null, null, null);String apnId = null;if (cur != null && cur.moveToFirst()) {apnId = cur.getString(cur.getColumnIndex("_id"));}Log.i("xml", "getCurApnId:" + apnId);return apnId;}public static APN getCurApnInfo(final Context context) {ContentResolver resoler = context.getContentResolver();// String[] projection = new String[] { "_id" };Cursor cur = resoler.query(PREFERRED_APN_URI, projection, null, null, null);APN apn = new APN();if (cur != null && cur.moveToFirst()) {apn.id = cur.getString(cur.getColumnIndex("_id"));apn.apn = cur.getString(cur.getColumnIndex("apn"));apn.type = cur.getString(cur.getColumnIndex("type"));}return apn;}public static void setApn(Context context, String id) {ContentResolver resolver = context.getContentResolver();ContentValues values = new ContentValues();values.put("apn_id", id);resolver.update(PREFERRED_APN_URI, values, null, null);Log.d("xml", "setApn");}// 获取WAP APNpublic static String getWapApnId(Context context) {ContentResolver contentResolver = context.getContentResolver();// 查询cmwapAPNCursor cur = contentResolver.query(APN_TABLE_URI, projection,"apn = \'cmwap\' and current = 1", null, null);// wap APN 端⼝不为空if (cur != null && cur.moveToFirst()) {do {String id = cur.getString(cur.getColumnIndex("_id"));String proxy = cur.getString(cur.getColumnIndex("proxy"));if (!TextUtils.isEmpty(proxy)) {Log.i("xml", "getWapApnId" + id);return id;}} while (cur.moveToNext());}return null;}public static String getNetApnId(Context context) {ContentResolver contentResolver = context.getContentResolver();Cursor cur = contentResolver.query(APN_TABLE_URI, projection,"apn = \'cmnet\' and current = 1", null, null);if (cur != null && cur.moveToFirst()) {return cur.getString(cur.getColumnIndex("_id"));}return null;}// 获取所有APNpublic static ArrayList<APN> getAPNList(final Context context) {ContentResolver contentResolver = context.getContentResolver();Cursor cr = contentResolver.query(APN_TABLE_URI, projection, null, null, null);ArrayList<APN> apnList = new ArrayList<APN>();if (cr != null && cr.moveToFirst()) {do {Log.d(TAG,cr.getString(cr.getColumnIndex("_id")) + ";"+ cr.getString(cr.getColumnIndex("apn")) + ";"+ cr.getString(cr.getColumnIndex("type")) + ";"+ cr.getString(cr.getColumnIndex("current"))+ ";"+ cr.getString(cr.getColumnIndex("proxy")));APN apn = new APN();apn.id = cr.getString(cr.getColumnIndex("_id"));apn.apn = cr.getString(cr.getColumnIndex("apn"));apn.type = cr.getString(cr.getColumnIndex("type"));apnList.add(apn);} while (cr.moveToNext());cr.close();}return apnList;}// 获取可⽤的APNpublic static ArrayList<APN> getAvailableAPNList(final Context context) { // current不为空表⽰可以使⽤的APNContentResolver contentResolver = context.getContentResolver();Cursor cr = contentResolver.query(APN_TABLE_URI, projection,"current is not null", null, null);ArrayList<APN> apnList = new ArrayList<APN>();if (cr != null && cr.moveToFirst()) {do {Log.d(TAG,cr.getString(cr.getColumnIndex("_id")) + ";"+ cr.getString(cr.getColumnIndex("apn")) + ";"+ cr.getString(cr.getColumnIndex("type")) + ";"+ cr.getString(cr.getColumnIndex("current"))+ ";"+ cr.getString(cr.getColumnIndex("proxy")));APN apn = new APN();apn.id = cr.getString(cr.getColumnIndex("_id"));apn.apn = cr.getString(cr.getColumnIndex("apn"));apn.type = cr.getString(cr.getColumnIndex("type"));apnList.add(apn);} while (cr.moveToNext());cr.close();}return apnList;}// ⾃定义APN包装类static class APN {String id;String apn;String type;@Overridepublic String toString() {return "id=" + id + ",apn=" + apn + ";type=" + type;}}}MMSSender类源代码View Code//发送类package com.rayray.util;import java.io.DataInputStream;import java.io.IOException;import .SocketException;import java.util.List;import org.apache.http.HttpEntity;import org.apache.http.HttpHost;import org.apache.http.HttpResponse;import org.apache.http.StatusLine;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpPost;import org.apache.http.conn.params.ConnRoutePNames;import org.apache.http.entity.ByteArrayEntity;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.params.BasicHttpParams;import org.apache.http.params.HttpConnectionParams;import org.apache.http.params.HttpParams;import org.apache.http.params.HttpProtocolParams;import org.apache.http.protocol.HTTP;import android.content.Context;import android.util.Log;/*** @author* @version创建时间:2012-2-1 上午09:32:54*/public class MMSSender {private static final String TAG = "MMSSender";// public static String mmscUrl = "";// public static String mmscProxy = "10.0.0.172";public static int mmsProt = 80;private static String HDR_VALUE_ACCEPT_LANGUAGE = "";private static final String HDR_KEY_ACCEPT = "Accept";private static final String HDR_KEY_ACCEPT_LANGUAGE = "Accept-Language";private static final String HDR_VALUE_ACCEPT = "*/*, application/vnd.wap.mms-message, application/vnd.wap.sic";public static byte[] sendMMS(Context context, List<String> list, byte[] pdu)throws IOException {System.out.println("进⼊sendMMS⽅法");// HDR_VALUE_ACCEPT_LANGUAGE = getHttpAcceptLanguage();HDR_VALUE_ACCEPT_LANGUAGE = HTTP.UTF_8;String mmsUrl = (String) list.get(0);String mmsProxy = (String) list.get(1);if (mmsUrl == null) {throw new IllegalArgumentException("URL must not be null.");}HttpClient client = null;try {// Make sure to use a proxy which supports CONNECT.// client = HttpConnector.buileClient(context);HttpHost httpHost = new HttpHost(mmsProxy, mmsProt);HttpParams httpParams = new BasicHttpParams();httpParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, httpHost);HttpConnectionParams.setConnectionTimeout(httpParams, 10000);client = new DefaultHttpClient(httpParams);HttpPost post = new HttpPost(mmsUrl);// mms PUD STARTByteArrayEntity entity = new ByteArrayEntity(pdu);entity.setContentType("application/vnd.wap.mms-message");post.setEntity(entity);post.addHeader(HDR_KEY_ACCEPT, HDR_VALUE_ACCEPT);post.addHeader(HDR_KEY_ACCEPT_LANGUAGE, HDR_VALUE_ACCEPT_LANGUAGE);post.addHeader("user-agent","Mozilla/5.0(Linux;U;Android 2.1-update1;zh-cn;ZTE-C_N600/ZTE-C_N600V1.0.0B02;240*320;CTC/2.0)AppleWebkit/530.17(KHTML,like Gecko) Version/4.0 Mobile Safari/530.17");// mms PUD ENDHttpParams params = client.getParams();HttpProtocolParams.setContentCharset(params, "UTF-8");System.out.println("准备执⾏发送");// PlainSocketFactory localPlainSocketFactory =// PlainSocketFactory.getSocketFactory();HttpResponse response = client.execute(post);System.out.println("执⾏发送结束,等回执。
MMS网络基本结构及工作原理
MMS网络基本结构及工作原理1. MMS 网络基本结构移动多媒体信息业务系统涵盖了多种类型的网络,并可以集成这些网络中现有的信息业务系统。
移动终端在多媒体信息业务环境(MMSE)中进行操作。
此环境既包括2.5G和3G网络,也有网络间的相互漫游等情况。
MMSE提供了所有相关的业务成份,如:信息的发送、存储、通知。
它们既可位于同一网络中或分布于不同的网络中。
在MMS服务投放市场以前,很多关于网络的实际准备工作必须预先完成。
在软、硬件的准备上除了可以接收MMS的终端外,还需要MMS中心、WAP网关、数据库服务器、增值服务(VAS)等。
◆多媒体信息中心(MMSC)在整个在多媒体信息业务环境(MMSE)中,多媒体信息中心(MMSC)是系统的核心。
由MMS服务器、MMS中继、信息存储器和数据库组成。
MMSC是MMS网络结构的核心,它提供存储和操作支持,允许终端到终端和终端到电子邮件的即时多媒体信息传送,同时支持灵活的寻址能力。
MMSC是将MMS信息从发送者传递到接收者的存储和转发网络元素。
MMSC的概念与SMSC相似,即服务器只在查找接收者电话的期间存储信息。
在找到接收电话以后,MMSC立即将多媒体消息转发给接收者,并且从MMSC删除此消息。
由于MMSC在能够发送的情况下不存储消息,因此它不是一个邮箱服务器。
MMSC是提供MMS服务所需的一个新的网络元素。
由于传输容量和界面需求都不同,SMSC的软件不能直接升级到MMSC。
另外,MMSC需要运行很多连接其它网络(如Internet)接口,以及提供增值服务所需的外部应用接口,MMSC 还应具备到Email的接口。
◆ WAP网关尽管用户对MMS的使用与SMS类似,但是MMS不能在SMS的传输信道进行传送,SMS的传输信道对于传送多媒体内容来说太窄了。
在协议层,MMS使用WAP无线会话协议(WSP)作为传输协议。
为了在MMS信息传输中使用WAP协议,需要一个WAP网关连接MMSC和无线WAP网络。
移动应用03_Android的SMSMMS应用开发
使用SmsManager类的 sendTextMessage()函数来发送短信。 sendTextMessage()函数发送一个短信和 一个PendingIntent对象。如下代码:
//收信人的地址 String destinationAddress = “8613811810000 ”; String emulatorAddress = “5554”; //仿真器地址 //准备一个PendingIntent对象 Intent in = new Intent("com.misoo.pk01.IGNORE_ME"); PendingIntent sentEvent = PendingIntent.getBroadcast(context, 0, in, 0);
使用Intent 发送E-mail
类似地,也可以使用Intent对象来发送E-mail。 如下面的代码:
Uri uri = Uri.parse("mailto:mike123@"); Intent in = new Intent(Intent.ACTION_SENDTO, uri); startActivity(in);
Step-4. 接着,选取<SMS>,并随意输入
电话号码,以及短信内容如下:
Step-5. 按下<Send>就送出短信了。 Step-6. 应用程序就会接收到短信了。 Step-7. 欲离开DDMS,可按下画面右上角 的小窗户图像,如下:
点选<Java>就返回Eclipse编辑画面了。
在模拟器环境里 应用程序使用API发送简讯
使用BroadcastReceiver类来接收短信。 当送来短信时,Android框架会调用 BroadcastReceiver类的onReceive()函数。
android系统自带短信程序源码部分分析
系统自带短信程序源码部分分析文章分类:移动开发这里并不打算对整个短信源码进行分析,完全是看了某部分代码后的自我总结。
我从GIT上clone了Conversation(即短信程序)的所有源码,结果编译不过。
不过这对分析它的源码并不造成太大的阻碍。
这里主要对短信主界面的数据和UI的交互角度进行分析,因为我自己写的短信程序在加入获取联系人头像功能后,程序启动时花费的查询时间太长。
虽然我也觉得系统默认的短信程序,甚至HandcentSMS,启动时间都不是很快。
(大概是我的机器性能太差)一、代码结构Conversation中整体结构主要包括com.android.mms.data和com.android.mms.ui,如名字所示,大概就是数据处理部分和UI部分。
数据部分主要是获取/缓存联系人信息、获取/缓存会话信息等。
ConversationList类是程序的主activity,派生于ListActivity,就是一个大的列表。
此外:ConversationListAdapter是这个ListView的adapter,派生于CursorAdapter;ConversationListItem是一个自定义的ViewGroup,派生于RelativeLayout,用于表示会话列表的每一个item;Conversation表示一个会话数据;Contact表示一个联系人;ContactList维护一个联系人列表;RecipientIdCache用于开线程读取一个特殊的表,该表映射会话数据到联系人信息,也就是通过Recipient就可以获取联系人信息。
二、UI结构这里的UI主要就是ConversationList/ConversationListAdapter/ConversationListItem三者之间的交互。
在layout中,conversation_list_item.xml作为这个ListView (ConversationList)的item定义,直接使用了ConversationListItem这个view:Java代码1.<com.android.mms.ui.ConversationListItem xmlns:android="http:///apk/res/android"2.android:layout_width="match_parent"3.android:layout_height="?android:attr/listPreferredItemHeight"4.android:background="@drawable/conversation_item_background_unread"5.android:paddingRight="10dip" >这个自定义item最重要的工作,就是将会话数据绑定到UI控件上,例如QuickContactBadge。
Android平台MMS_SMS的PDU编码研究
软件Chi na lntegr ated Cir c ultAn d r o i d 平台MM S /S M S 的 PDU 编码研究杨小见,任家富,黄美传(成都理工大学信息科学与技术学院,四川 成都,610059)摘要:因为当前 Google 的 Android 操作系统大为盛行,所以像短信、通话这些移动平台的基本通信功能就显得格外关键。
PDU 编码,作为一种较为先进的信息编码方式正为广大技术开发人员所接受,本文研究 了 SMS 发送 PDU 的编码过程,并且结合实例对编码原理进行剖析,验证了 PDU 编码的可行性。
关键字:Android;SMS/MMS;PDUPDU encoding of MMS/SM S base on Android OSYANG Xiao-jian ,REN Jia-fu ,HUANG Mei-chuan(College of Information Science and Technology,CDUT,Chengdu 601159,china )A b s t ract :A s G oog l e ’s A nd r o i d OS is be c o m i n g more and more p o pu l a r , the basic fun c t i o ns of m o b il e p l atf o r m , su c h as SMS and phone turn out to be particularly i mp o r tant . S o , PDU en c o d i n g is widely accepted by deve l o pe r s . T h i s paper shows how to encode and send messa g es , and presents the feasibility of PDU en c o d i n g based on the en c o d i n g the o r y .Key w ord s :A nd r o i d ; SMS /MMS ; PDU1 引言效地解决了通信编码[1]的很多问题,比起传统的编码 方式有着较大的优势。
MMS协议解析
MMS协议解析2 (原创)关键词:MMS 协议解析转载请注明出处,谢谢!01 至ServerPrefix 1 f0 f0 f0 f0 - 标志(见标志段)Prefix 2 0b 00 04 00Then 1c 00 03 00结构题定义如下。
功能:发送初始链接信息,包含播放器的版本号、客户端GUID(随机产生)和要连接的服务器地址。
这个命令是在协议初始化之初发送的。
它发送本地信息给服务器。
Unicode数据字符串由以下信息组成:“NSPlayer/7.0.0.1956; {128比特16进制文本客户端GUID }; Host: ” + 0x00 + 全零隐藏数据域(可选项)。
注意:客户端GUID是随机生成的,具体内容见'locally generated GUIDs'.'Host' 域为可选字段。
只在Media Player 7.0及后续版本中使用。
播放器名称必须以“NSPlayer”开始,如果服务器收到其他名称,将会自动发送名为'Upgrade Your Player'缺省的电影。
这是一个15秒的教你如何升级的电影。
在NSPlayer之后可以接任意的东西。
例如像/7.0.0.1956的版本号。
MediaPlayer7.0及后续版本才支持'MMS Proxy Server'选项。
'Host'域指明实际流媒体服务器的域名或者IP地址,这同是否使用代理并不相关。
代理服务器使用这个主机地址连接到流媒体服务器。
这就是在7.0以前版本里面没有'host'域的原因。
01 至ClientPrefix 1 00 00 00 00 - 错误码Prefix 2 f0 f0 f0 f0 - 标志(详见标志段)结构体定义如下:Unicode字符串长度在结构体中给出。
当域不需要时length=0。
长度的统计是以两个byte为单位。
MMS发送流程(代码版)android
MMS发送流程(代码版)Android2.2packages/apps/Mms1. 点击发送按钮Src//android/mms/ui/poseMessageActivity.javapublic void onClick(View v) {if ((v == mSendButton) && isPreparedForSending()) {confirmSendMessageIfNeeded(); //确认是否需要发送短信—-》}}2.src//android/mms/ui/poseMessageActivity.javaprivate void confirmSendMessageIfNeeded() {if (!isRecipientsEditorVisible()) { //编辑联系人不可见时,也就是给已存在会话的联系人发送短信时sendMessage(true);return;}boolean isMms = mWorkingMessage.requiresMms(); //是否需要以彩信形式发送if (mRecipientsEditor.hasInvalidRecipient(isMms)) {//是否含有不合法的收件人if (mRecipientsEditor.hasValidRecipient(isMms)) {//有合法的和不合法的,弹出尝试发送对话框String title = getResourcesString(R.string.has_invalid_recipient,mRecipientsEditor.formatInvalidNumbers(isMms));new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(title).setMessage(R.string.invalid_recipient_message).setPositiveButton(R.string.try_to_send,newSendIgnoreInvalidRecipientListener()).setNegativeButton(R.string.no, new CancelSendingListener()).show();} else {//如果全是不合法的联系人,提示不能发送信息new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(R.string.cannot_send_message).setMessage(R.string.cannot_send_message_reason).setPositiveButton(R.string.yes, new CancelSendingListener()).show();}} else {//判断收件人没有问题,接着发送信息 --》sendMessage(true);}}3. src//android/mms/ui/poseMessageActivity.javaprivate void sendMessage(boolean bCheckEcmMode) {Log.v(TAG, "sendMessage");if (bCheckEcmMode) {// TODO: expose this in telephony layer for SDK buildString inEcm =SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE); //判断是否处于紧急拨号模式,得到的inEcm一般为空Log.v(TAG, "inEcm = " + inEcm);if (Boolean.parseBoolean(inEcm)) {try {startActivityForResult(newIntent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS,null),REQUEST_CODE_ECM_EXIT_DIALOG);return;} catch (ActivityNotFoundException e) {// continue to send messageLog.e(TAG, "Cannot find EmergencyCallbackModeExitDialog", e);}}}if (!mSendingMessage) {// send can change the recipients. Make sure we remove the listeners firstand then add// them back once the recipient list has settled.removeRecipientsListeners(); //取消对收件人的监听mWorkingMessage.send(); //发送信息—-》mSentMessage = true;mSendingMessage = true;addRecipientsListeners(); //重新添加收件人监听}// But bail out if we are supposed to exit after the message is sent.if (mExitOnSent) {//如果mExitOnSent为true,信息发送完成后退出Activityfinish();}}4. src//android/mms/data/WorkingMessage.java/*** Send this message over the network. Will call back with onMessageSent() once* it has been dispatched to the telephonystack. This WorkingMessage object is* no longer useful after this method hasbeen called.*/public void send() {if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {LogTag.debug("send");}// Get ready to write to disk.prepareForSave(true /* notify */);//主要做一下同步收件人和WorkingMessage,彩信时在准备其他一些东西// We need the recipient list for both SMS and MMS.final Conversation conv = mConversation;String msgTxt = mText.toString();Log.v(TAG, "msgText = " + msgTxt);if (requiresMms()|| addressContainsEmailToMms(conv, msgTxt)) {// Make local copies of the bits we need for sending a message,// because we will be doing it off of the main thread, which will// immediately continue on to resetting some of this state.final Uri mmsUri = mMessageUri; //如果第一次发送,此时mmsUri为null,如果是重发,则是草稿箱的地址 mMessageUri =content://mms/drafts/1final PduPersister persister = PduPersister.getPduPersister(mContext);final SlideshowModel slideshow = mSlideshow;final SendReq sendReq = makeSendReq(conv,mSubject);// Do the dirty work of sending the message off of the main UI thread.new Thread(new Runnable() {public void run() {// Make sure the text in slide 0 is no longer holding onto a reference to// the text in the message text box.slideshow.prepareForSend();sendMmsWorker(conv, mmsUri, persister, slideshow, sendReq);}}).start();}else {// Same rules apply as above.final String msgText = mText.toString();//取出短消息Log.v(TAG, "msgText = " + msgText);new Thread(new Runnable() {public void run() {preSendSmsWorker(conv, msgText);//发送信息--》}}).start();}// update the Recipient cache with the new to address, if it's differentRecipientIdCache.updateNumbers(conv.getThreadId(),conv.getRecipients());// Mark the message as discarded because it is "off the market"after being sent.mDiscarded = true;}5. src//android/mms/data/WorkingMessage.javaprivate void sendMmsWorker(Conversation conv, Uri mmsUri, PduPersisterpersister, SlideshowModel slideshow, SendReq sendReq) {Log.v(TAG, "sendMmsWorker");// If user tries to send the message, it's a signal the inputtedtext is what they wanted.erAcceptedImeText(mContext);// First make sure we don't have too many outstanding unsent message.Cursor cursor = null;try {cursor = SqliteWrapper.query(mContext, mContentResolver,Mms.Outbox.CONTENT_URI,MMS_OUTBOX_PROJECTION, null, null, null);if (cursor != null) {//如果MMS_OUTBOX里有未发送的彩信,并且总的大小已经超过了彩信的最大限制,则取消此次发送,并存入草稿箱Log.v(TAG, "query Mms.Outbox.CONTENT_URI is not empty");long maxMessageSize = MmsConfig.getMaxSizeScaleForPendingMmsAllowed()*MmsConfig.getMaxMessageSize();Log.v(TAG, "MmsConfig.getMaxSizeScaleForPendingMmsAllowed() =" + MmsConfig.getMaxSizeScaleForPendingMmsAllowed());Log.v(TAG, "MmsConfig.getMaxMessageSize()() = " +MmsConfig.getMaxMessageSize());long totalPendingSize = 0;while (cursor.moveToNext()) {totalPendingSize +=cursor.getLong(MMS_MESSAGE_SIZE_INDEX);Log.v(TAG, "totalPendingSize = " + totalPendingSize);}if (totalPendingSize >= maxMessageSize) {unDiscard(); // itwasn't successfully sent. Allow it to be saved as a draft.mStatusListener.onMaxPendingMessagesReached();return;}}else{Log.v(TAG, "query Mms.Outbox.CONTENT_URI is empty");}} finally {if (cursor != null) {cursor.close();}}mStatusListener.onPreMessageSent();// Make sure we are still using the correct thread ID for our// recipient set.long threadId = conv.ensureThreadId();if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {LogTag.debug("sendMmsWorker: update draft MMS message " + mmsUri); }if (mmsUri == null) {//如果是首次发送,先把彩信保存入草稿箱// Create a new MMS message if one hasn't been made yet.Log.v(TAG, "mmsUri == null and startcreateDraftMmsMessage");mmsUri = createDraftMmsMessage(persister,sendReq, slideshow);} else {// Otherwise, sync the MMS message in progress to disk.Log.v(TAG, "mmsUri = " + mmsUri);Log.v(TAG, "updateDraftMmsMessage");updateDraftMmsMessage(mmsUri,persister, slideshow, sendReq); }// Be paranoid and clean any draft SMS up. deleteDraftSmsMessage(threadId);// Resize all the resizeable attachments (e.g. pictures) to fit // in the remaining space in the slideshow.int error = 0;try {slideshow.finalResize(mmsUri);} catch (ExceedMessageSizeException e1) {error = MESSAGE_SIZE_EXCEEDED;} catch (MmsException e1) {error = UNKNOWN_ERROR;}if (error != 0) {markMmsMessageWithError(mmsUri);mStatusListener.onAttachmentError(error);return;}MessageSender sender = new MmsMessageSender(mContext, mmsUri, slideshow.getCurrentMessageSize());try {if (!sender.sendMessage(threadId)) {// The message was sent through SMS protocol, we should// delete the copy which was previously saved in MMS drafts.SqliteWrapper.delete(mContext, mContentResolver, mmsUri, null, null);}// Make sure this thread isn't over the limits in message countRecycler.getMmsRecycler().deleteOldMessagesByThreadId(mContext, threadId);} catch (Exception e) {Log.e(TAG, "Failed to send message: " + mmsUri + ",threadId=" + threadId, e);}mStatusListener.onMessageSent();}6.src//android/mms/transaction/MmsMessageSender.javapublic boolean sendMessage(long token) throws MmsException {// Load the MMS from the message uriPduPersister p = PduPersister.getPduPersister(mContext);GenericPdu pdu = p.load(mMessageUri);if (pdu.getMessageType() != PduHeaders.MESSAGE_TYPE_SEND_REQ){throw new MmsException("Invalid message: " +pdu.getMessageType());}SendReq sendReq = (SendReq)pdu;// Update headers.updatePreferencesHeaders(sendReq);// MessageClass.sendReq.setMessageClass(DEFAULT_MESSAGE_CLASS.getBytes());// Update the 'date' field of the message before sending it.sendReq.setDate(System.currentTimeMillis()/ 1000L);sendReq.setMessageSize(mMessageSize);p.updateHeaders(mMessageUri, sendReq);// Move the message into MMS Outboxp.move(mMessageUri, Mms.Outbox.CONTENT_URI);// Start MMS transaction serviceSendingProgressTokenManager.put(ContentUris.parseId(mMessageUri), token);mContext.startService(new Intent(mContext, TransactionService.class));return true;}7.src//android/mms/transaction/TransactionService.javaOverridepublic int onStartmand(Intent intent, int flags, int startId) {Log.v(TAG, "onStartmand");if (intent == null) {return Service.START_NOT_STICKY;}mConnMgr = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);boolean noNetwork =!isNetworkAvailable();if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "onStart: #" + startId + ": " + intent.getExtras() + " intent=" + intent);Log.v(TAG, " networkAvailable=" + !noNetwork);}Log.v(TAG, "getAction is " + intent.getAction());if (ACTION_ONALARM.equals(intent.getAction())|| (intent.getExtras() == null)) {Log.v(TAG, "ACTION_ONALARM.equals(intent.getAction()) ||(intent.getExtras() == null)");// Scan database to find all pending operations.Cursor cursor = PduPersister.getPduPersister(this).getPendingMessages(System.currentTimeMillis());if (cursor != null) {try {int count = cursor.getCount();if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "onStart: cursor.count=" + count);}if (count == 0) {if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "onStart: no pending messages. Stoppingservice.");}RetryScheduler.setRetryAlarm(this);stopSelfIfIdle(startId);return Service.START_NOT_STICKY;}int columnIndexOfMsgId=cursor.getColumnIndexOrThrow(PendingMessages.MSG_ID);int columnIndexOfMsgType =cursor.getColumnIndexOrThrow(PendingMessages.MSG_TYPE);if (noNetwork) {// Make sure we register for connection state changes.if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "onStart: registerForConnectionStateChanges");}MmsSystemEventReceiver.registerForConnectionStateChanges(getApplicationContext());}while (cursor.moveToNext()) {int msgType =cursor.getInt(columnIndexOfMsgType);int transactionType =getTransactionType(msgType);Log.v(TAG, "msgType = " + msgType);Log.v(TAG, "transactionType = " + transactionType);if (noNetwork) {onNetworkUnavailable(startId, transactionType);return Service.START_NOT_STICKY;}switch (transactionType){case -1:break;case Transaction.RETRIEVE_TRANSACTION:// If it's a transiently failed transaction,// we should retry it in spite of current// downloading mode.int failureType =cursor.getInt(cursor.getColumnIndexOrThrow(PendingMessages.ERROR_TYPE));if (!isTransientFailure(failureType)){break;}// fall-throughdefault:Uri uri =ContentUris.withAppendedId(Mms.CONTENT_URI,cursor.getLong(columnIndexOfMsgId));TransactionBundle args = new TransactionBundle(transactionType, uri.toString());// FIXME: We use the same startId for all MMs.launchTransaction(startId, args, false);break;}}} finally {cursor.close();}} else {if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "onStart: no pending messages. Stoppingservice.");}RetryScheduler.setRetryAlarm(this);stopSelfIfIdle(startId);}} else {if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "onStart: launch transaction...");}// For launching NotificationTransaction and test purpose.TransactionBundle args = newTransactionBundle(intent.getExtras());launchTransaction(startId, args,noNetwork);}return Service.START_NOT_STICKY;}8. src//android/mms/transaction/TransactionService.javaprivate void launchTransaction(int serviceId,TransactionBundle txnBundle, boolean noNetwork) {Log.v(TAG, "launchTransaction");if (noNetwork) {Log.w(TAG, "launchTransaction: no network error!");onNetworkUnavailable(serviceId,txnBundle.getTransactionType());return;}Message msg = mServiceHandler.obtainMessage(EVENT_TRANSACTION_REQUEST);msg.arg1 = serviceId;msg.obj = txnBundle;if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "launchTransaction: sending message " + msg);}mServiceHandler.sendMessage(msg);}9. src//android/mms/transaction/TransactionService.javaprivate final class ServiceHandler extends Handler {public ServiceHandler(Looper looper) {super(looper);}/*** Handle ining transactionrequests.* The ining requests are initiatedby the MMSC Server or by the* MMS Client itself.*/Overridepublic void handleMessage(Messagemsg) {if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "Handling ining message: " + msg);}Transaction transaction = null;switch (msg.what) {case EVENT_QUIT:getLooper().quit();return;case EVENT_CONTINUE_MMS_CONNECTIVITY:synchronized (mProcessing) {if (mProcessing.isEmpty()) {return;}}if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "handle EVENT_CONTINUE_MMS_CONNECTIVITYevent...");}try {int result =beginMmsConnectivity();if (result != Phone.APN_ALREADY_ACTIVE){Log.v(TAG, "Extending MMS connectivity returned " + result +" instead of APN_ALREADY_ACTIVE");// Just wait for connectivity startup without// any newrequest of APN switch.return;}} catch (IOException e) {Log.w(TAG, "Attempt to extend use of MMS connectivityfailed");return;}// Restart timersendMessageDelayed(obtainMessage(EVENT_CONTINUE_MMS_CONNECTIVITY),APN_EXTENSION_WAIT);return;case EVENT_DATA_STATE_CHANGED:/** If we are being informedthat connectivity has been established * to allow MMS traffic,then proceed with processing the pending * transaction, if any.*/if (mConnectivityListener == null) {return;}NetworkInfo info = mConnectivityListener.getNetworkInfo();if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "Handle DATA_STATE_CHANGED event: " + info);}// Check availability of the mobile network.if ((info == null) || (info.getType() !=ConnectivityManager.TYPE_MOBILE_MMS)) {if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, " type isnot TYPE_MOBILE_MMS, bail");}return;}if (!info.isConnected()) {if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, " TYPE_MOBILE_MMS not connected, bail");}return;}TransactionSettings settings = newTransactionSettings(TransactionService.this,info.getExtraInfo());// If this APN doesn't have an MMSC, wait for one that does.if (TextUtils.isEmpty(settings.getMmscUrl())){if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, " empty MMSCurl, bail");}return;}// Set a timer to keep renewing our "lease" on the MMSconnection sendMessageDelayed(obtainMessage(EVENT_CONTINUE_MMS_CONNECTIVITY),APN_EXTENSION_WAIT);processPendingTransaction(transaction, settings);return;case EVENT_TRANSACTION_REQUEST://响应请求Log.v(TAG, "EVENT_TRANSACTION_REQUEST");int serviceId = msg.arg1;try {TransactionBundle args= (TransactionBundle) msg.obj;TransactionSettingstransactionSettings;// Set the connection settings for this transaction.// If these have not been set in args, load thedefault settings.String mmsc =args.getMmscUrl();if (mmsc != null) {transactionSettings= new TransactionSettings(mmsc,args.getProxyAddress(), args.getProxyPort());} else {transactionSettings= new TransactionSettings(TransactionService.this, null);}int transactionType =args.getTransactionType();Log.v(TAG, "transactionType = " + transactionType);if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "handleEVENT_TRANSACTION_REQUEST:transactionType=" +transactionType);}// Create appropriate transactionswitch (transactionType){case Transaction.NOTIFICATION_TRANSACTION:String uri =args.getUri();if (uri != null) {transaction= new NotificationTransaction(TransactionService.this, serviceId,transactionSettings, uri);} else {// Now it's only used for test purpose.byte[] pushData =args.getPushData();PduParserparser = new PduParser(pushData);GenericPdu ind= parser.parse();int type =PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;if ((ind != null) &&(ind.getMessageType() == type)) {transaction = newNotificationTransaction(TransactionService.this, serviceId,transactionSettings, (NotificationInd) ind);} else {Log.e(TAG, "Invalid PUSH data.");transaction = null;return;}}break;case Transaction.RETRIEVE_TRANSACTION:transaction = newRetrieveTransaction(TransactionService.this, serviceId,transactionSettings, args.getUri());break;case Transaction.SEND_TRANSACTION://根据transactiontype响应发送彩信Log.v(TAG, "Transaction.SEND_TRANSACTION");transaction = new SendTransaction(TransactionService.this, serviceId,transactionSettings, args.getUri());break;case Transaction.READREC_TRANSACTION:transaction = newReadRecTransaction(TransactionService.this, serviceId,transactionSettings, args.getUri());break;default:Log.w(TAG, "Invalidtransaction type: " + serviceId);transaction = null;return;}if (!processTransaction(transaction)) {transaction = null;return;}if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "Started processing of ining message: " + msg);}} catch (Exception ex) {Log.w(TAG, "Exception occurred while handling message: " + msg, ex);if (transaction != null) {try {transaction.detach(TransactionService.this);if (mProcessing.contains(transaction)){synchronized (mProcessing) {mProcessing.remove(transaction);}}} catch (Throwable t) {Log.e(TAG, "Unexpected Throwable.", t);} finally {// Set transaction to null to allow stopping the// transaction service.transaction = null;}}} finally {if (transaction == null) {if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "Transaction was null. Stopping self: " + serviceId);}endMmsConnectivity();stopSelf(serviceId);}}return;case EVENT_HANDLE_NEXT_PENDING_TRANSACTION:processPendingTransaction(transaction, (TransactionSettings) msg.obj);return;default:Log.w(TAG, "what=" + msg.what);return;}}10. src//android/mms/transaction/TransactionService.java/*** Internal method to begin processinga transaction.* param transaction the transaction. Must not be{code null}.* return {code true} if process hasbegun or will begin. {code false}* if the transaction should bediscarded.* throws IOException if connectivityfor MMS traffic could not be* established.*/private boolean processTransaction(Transaction transaction) throws IOException { // Check if transaction already processingLog.v(TAG, "processTransaction");synchronized (mProcessing) {for (Transaction t : mPending) {if (t.isEquivalent(transaction)) {if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "Transaction already pending: " +transaction.getServiceId());}return true;}}for (Transaction t : mProcessing) {if (t.isEquivalent(transaction)) {if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "Duplicated transaction: " +transaction.getServiceId());}return true;}}/** Make sure that the networkconnectivity necessary* for MMS traffic is enabled.If it is not, we need* to defer processing thetransaction until* connectivity is established.*/if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "processTransaction: callbeginMmsConnectivity...");}int connectivityResult = beginMmsConnectivity();if (connectivityResult == Phone.APN_REQUEST_STARTED){mPending.add(transaction);if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "processTransaction: connResult=APN_REQUEST_STARTED," +"defer transaction pending MMS connectivity");}return true;}if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "Adding transaction to 'mProcessing' list: " + transaction);}mProcessing.add(transaction);}// Set a timer to keep renewing our "lease" on the MMSconnectionsendMessageDelayed(obtainMessage(EVENT_CONTINUE_MMS_CONNECTIVITY),APN_EXTENSION_WAIT);if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {Log.v(TAG, "processTransaction: starting transaction " + transaction);}// Attach to transaction and process ittransaction.attach(TransactionService.this);transaction.process();return true;}11.src//android/mms/transaction/SendTransaction.javaOverridepublic void process() {Log.v(TAG, "process");mThread = new Thread(this);mThread.start();}12. src//android/mms/transaction/SendTransaction.javapublic void run() {Log.v(TAG, "run()");try {RateController rateCtlr =RateController.getInstance();if (rateCtlr.isLimitSurpassed() &&!rateCtlr.isAllowedByUser()) { Log.e(TAG, "Sending rate limit surpassed.");return;}// Load M-Send.req from outboxPduPersister persister = PduPersister.getPduPersister(mContext);SendReq sendReq = (SendReq)persister.load(mSendReqURI);// Update the 'date' field of the PDU right before sending it.long date = System.currentTimeMillis() /1000L;sendReq.setDate(date);// Persist the new date value into database.ContentValues values = new ContentValues(1);values.put(Mms.DATE, date);SqliteWrapper.update(mContext, mContext.getContentResolver(),mSendReqURI, values, null, null);// fix bug 2100169: insert the 'from' address per specString lineNumber = MessageUtils.getLocalNumber();if (!TextUtils.isEmpty(lineNumber)) {sendReq.setFrom(new EncodedStringValue(lineNumber));}// Pack M-Send.req, send it, retrieve confirmation data, and parse itlong tokenKey = ContentUris.parseId(mSendReqURI);byte[] response = sendPdu(SendingProgressTokenManager.get(tokenKey),new Pduposer(mContext,sendReq).make());//发送彩信SendingProgressTokenManager.remove(tokenKey);if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {String respStr = new String(response);Log.d(TAG, "[SendTransaction] run: send mms msg (" + mId + "),resp=" + respStr);}SendConf conf = (SendConf)new PduParser(response).parse();if (conf == null) {Log.e(TAG, "No M-Send.conf received.");}// Check whether the responding Transaction-ID is consistent// with the sent one.byte[] reqId = sendReq.getTransactionId();byte[] confId = conf.getTransactionId();if (!Arrays.equals(reqId, confId)) {Log.e(TAG, "Inconsistent Transaction-ID: req="+ new String(reqId) + ", conf=" + new String(confId));return;}// From now on, we won't save the whole M-Send.conf into// our database. Instead, we just save some interesting fields// into the related M-Send.req.values = new ContentValues(2);int respStatus = conf.getResponseStatus();values.put(Mms.RESPONSE_STATUS,respStatus);if (respStatus != PduHeaders.RESPONSE_STATUS_OK){SqliteWrapper.update(mContext, mContext.getContentResolver(),mSendReqURI, values, null, null);Log.e(TAG, "Server returned an error code: " + respStatus);return;}String messageId = PduPersister.toIsoString(conf.getMessageId());values.put(Mms.MESSAGE_ID,messageId);SqliteWrapper.update(mContext, mContext.getContentResolver(),mSendReqURI, values, null, null);// Move M-Send.req from Outbox into Sent.Uri uri = persister.move(mSendReqURI, Sent.CONTENT_URI);mTransactionState.setState(TransactionState.SUCCESS);mTransactionState.setContentUri(uri);} catch (Throwable t) {Log.e(TAG, Log.getStackTraceString(t));} finally {if (mTransactionState.getState() != TransactionState.SUCCESS) {mTransactionState.setState(TransactionState.FAILED);mTransactionState.setContentUri(mSendReqURI);Log.e(TAG, "Delivery failed.");}notifyObservers();}}13.src//android/mms/transaction/Transaction.java/*** A mon method to send a PDU to MMSC.** param token The token to identify the sendingprogress.* param pdu A byte array which contains the dataof the PDU.* return A byte array which containsthe response data.* If an HTTP error code is returned, an IOException will be thrown.* throws IOException if any erroroccurred on network interface or* an HTTP error code(>=400) returned from the server.*/protected byte[] sendPdu(long token, byte[]pdu) throws IOException { return sendPdu(token, pdu, mTransactionSettings.getMmscUrl());}14. src//android/mms/transaction/Transaction.javaprotected byte[] sendPdu(long token, byte[] pdu, StringmmscUrl) throws IOException { ensureRouteToHost(mmscUrl, mTransactionSettings);return HttpUtils.httpConnection(mContext, token,mmscUrl,pdu, HttpUtils.HTTP_POST_METHOD,mTransactionSettings.isProxySet(),mTransactionSettings.getProxyAddress(),mTransactionSettings.getProxyPort());//通过网络发送彩信,AP层的最后实现}。
android应用程序源码结构分析
1、src文件夹存放源码。
2、gen下有跟src中一样的包文件,内部有一个名为R.java类,它是自动生成的一个类;该目录不用我们开发人员维护,但又非常重要的目录。
该目录用来存放由Android 开发工具所生成的目录。
该目录下的所有文件都不是我们创建的,而是由系统自动生成的。
这个R,javav文件是只读类型,用户一般式不需要修改的!R.java 文件中默认有attr 、drawable 、layout 、string 等四个静态内部类,每个静态内部类分别对应一种资源,layout 静态内部类对应layout 中的界面文件main.xml,其中每个静态内部类中的静态常量分别定义一条资源标识符,如“ publicstatic final int main=0x7f030000; ” 用一个十六进制的数来代表常量,当开发者在res文件夹下添加或删除任何一个文件或一个属性,R.java都会随之进行更新!3、android 2.3.3/ 文件夹表明了开发环境的版本,内部存放Android 自身的jar 包。
4、assets/该目录用来存放应用中用到的类似于视频文件、MP3 一些媒体文件。
5、res/ 该目录为资源目录。
该目录可以存放一些应用图标、界面文件、应用中用到的文字信息等。
res/目录下有三个dawable 文件夹,区别只是将图标按分辨率高低来放入不同的目录中,drawable-hdpi存放高分辨率的图标,drawable-mdpi存放中等分辨率的图标,drawable-ldpi存放低分辨率的图标。
程序运行时可以根据手机分辨率的高低选取相应目录下的图标。
值得注意的是:如果是老版本的,比如是1.5版本的,res下的drawable文件夹只有一个,如需修改需要手动添加,res/目录下layout/的文件main.xml是布局文件,main.xml<? xml version = "1.0" encoding = "utf-8"?>< LinearLayout xmlns:android = "/apk/res/android"android:orientation = "vertical"android:layout_width = "fill_parent"android:layout_height = "fill_parent">< TextViewandroid:layout_width = "fill_parent"android:layout_height = "wrap_content"android:text = "@string/hello"/></ LinearLayou t ><LinearLayout>“LinearLayout ”代表的是“ 线性布局” ,所谓线性布局就是在该元素下的所有子元素会根据其“ orientation ” 属性的值来决定是按行或者是按列逐个显示。
MMS协议原理及实现
收稿日期 :2003212212
© 1994-2007 China Academic Journal Electronic Publishing House. All rights reserved.
图 4 MMS 用于控制进程动作的有限状态机
图 4 是 MMS 用于控制进程动作的有限状态 机 。用有限状态机来记录状态间的变化转移 ,并执 行相应的操作 ,可以大大简化程序的设计 ,提高编程 效率 ,更能增强源代码的可读性 ,是实现 MMS 软件 的理想方法 。具体说明为 :圆圈代表 MMS 的状态 ; 箭头表示状态之间的转换 ;箭头上的文字表示事件 ; 加粗的文字表示是接收到的事件 ;未加粗的文字为
此状态机是作者根据 socket 编程模型进行设计 的 ,具体实现时可以根据底层 WAP 协议 、上层应用 程序及嵌入式操作系统的特点进行相应的调整 。 如 :在发送时 ,收到 M2Send. conf 后 ,即使接收投递 报告 ,MMS 也不一定要进入 ENDS 状态 ,因为如果 MMS 接收方不允许网关发送投递报告的话 ,发送 端就不会收到此消息 ,这样状态机将不会结束 。此 时可 以 让 应 用 程 序 保 留 发 送 MMS 消 息 时 的 X2 MMS2Transaction2ID ,当投递报告来时 ,把它直接交 给应用 程 序 , 由 应 用 程 序 根 据 此 消 息 的 X2MMS2 Transaction2ID 查找是与哪个 MMS 消息相对应 。 2. 2 MMS 编/ 解码器
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
从软件的功能角度来讲,Mms分为对话列表,消息列表,短信编辑,彩信编辑,短信显示,彩信显示和配置。
从实现的角度来看,它分为GUI展示层,发送/接收,彩信解析,彩信附件,信息数据等,这些分类对应着源码中的各种包。
源码导航Mms的源码的位置在于android/packages/apps/Mms其中Mms/src/com/android/mms里面都是Mms相关的代码,而Mms/src/org/w3c/dom里面是一个类库,主要用于彩信格式的解析和显示。
这里主要讲一下Mms/src/com/android/mms下面的一些包和类的主要用途。
ui---GUI展示层,用于展示对话列表,消息列表,消息编辑页,彩信附件编辑,彩信展示,播放幻灯片。
负责直接与用户交互。
∙ConversationListAdapter.java---对话列表的Adapter用于给显示层ConversationList绑定数据。
∙ConversationListItemData.java---代表对话列表中的每一项的数据结构,里面含有要在对话列表中展示的信息。
∙ConversationList.java------这是对话列表的显示窗口Activity,它是一个ListActivity,这几个类都是对话列表的相关类,用于显示,编辑和管理所有的对话。
∙ComposeMessageActivity.java----这个是核心的窗口Activity,编辑信息,显示一条对话Thread中的所有往来信息。
MessageListView会加在其上面,另外,AttachmentEditor也会加在其上面。
这个Activity也负责响应外部应用程序,发送SENDTO或SEND等请求Intent,比如外部应用想要发送信息,等就由这个Activity来响应。
∙MessageItem.java---代表一个信息的抽象数据,它包含了信息相关的所有内容,比如信息的主题,消息内容,来信地址,附件内容等等。
它的所有数据都是公共的内部成员,都可以直接访问。
∙MessageListAdapter.java---用于给消息列表显示层(由ComposeMessageActivity创建,绑定到MessageListView上)绑定数据。
∙MessageListView.java---用于显示消息列表,继承自ListView,其生命周期由ComposeMessageActivity来控制,显示与否也由它来控制。
∙MessageListItem.java---是一个布局,用于显示和控制消息列表中的每一个消息的显示。
∙AttachmentTypeSelectorAdapter.java---用于添加附件件时的一个支持的附件列表,它就是一个菜单。
∙AttachmentEditor.java---用于在编辑MMS彩信信息时,显示已添加的附件,它的生命周期由ComposeMessageActivity来控制,显示与否也是由ComposeMessageActivity来控制,当有彩信附件时,它就会显示,否则就被Hide。
它是一个布局管理器,管理着下面四个布局,根据附件的类型动态的显示下面四个View中的某一个。
∙AudioAttachmentView.java---在编辑信息器中用于显示音频附件,它是继承自线性布局。
并不在代码中直接使用,而是在布局文件中来当成布局管理器使用。
∙ImageAttachmentView.java---在编辑信息器中用于显示图片附件,它是继承自线性布局。
并不在代码中直接使用,而是在布局文件中来当成布局管理器使用。
∙SlideshowAttachmentView.java---在编辑信息器中用于显示幻灯片附件,它是继承自线性布局。
并不在代码中直接使用,而是在布局文件中来当成布局管理器使用。
∙VideoAttachmentView.java---在编辑信息器中用于显示视频附件,它是继承自线性布局。
并不在代码中直接使用,而是在布局文件中来当成布局管理器使用。
∙SlideshowActivity.java—用来全屏播放幻灯片,也即幻灯片的展示,因为彩信的创建和播放都是以幻灯片的方式进行的,也即一张一张的,每张上面可以文字,图片,视频和音频,每一张有浏览时长。
∙SlideshowEditActivity.java---以列表方式管理幻灯片,也即是把所有的幻灯片用列表显示出来,用户可添加一页幻灯片,也可以点击进入编辑某页幻灯片,用于创建和编辑幻灯片。
∙SlideshowEditor.java---用于编辑某页幻灯片,比如添加元素,删除元素和替换元素,这里的元素可以是图片,视频,音频和文字。
也可以用于编辑整页幻灯片,比如删除某页幻灯片,调整这页幻灯片在所有幻灯片中的位置等。
它是一个具体操作幻灯片的封装,SlideEditorActivity创建它并使用它来完成纪灯片的编辑。
∙SlideshowPresenter.java---用于展示所有的幻灯片,也就是播放所有的幻灯片。
由SlideshowActivity来创建和使用。
∙SlideViewInterface.java---定义了一些用于显示一页幻灯片中的内容的接口,如设置图像,设置视频,设置音频,播放视频,播放音频,暂停,随机定位等等。
附件显示的View:AudioAttachmentView,ImageAttachmentView,SlideshowAttachmentView和VideoAttachmentView均实现了此接口,这样AttachmentEditor就可以用统一的接口来控制内容的播放,而不用关心具体的内容是什么。
∙SlideEditorActivity.java---用于编辑某页幻灯片,比如添加音频,添加视频,添加图像,添加文字等。
它只是提供用户界面,让用户来操作各种按扭以达到添加元素,替换元素或是删除元素。
而对具体的幻灯片的操作是通过SlideshowEditor来完成的,它主要负责与用户交互。
∙SlideListItemView.java--- SlideshowEditActivity中列表的每一项的布局管理,继承自LinearLayout。
∙MmsThumbnailPresenter.java---用于在消息列表中,显示彩信的缩略图,因为彩信的内容不固定,可能是图片,可能是音频,可能是视频也可能是幻灯片,所以用这个类来处理并显示彩信的缩略图。
∙MessagingPreferenceActivity.java---Mms的配置信息编辑器,用来编辑和更改配置信息,继承息PreferenceActivity。
它负责与用户交互,显示和更改配置。
在Mms启动时,MmsConfig会从SharedPreference中读出配置信息,在运行时其他的类的配置信息都是从MmsConfig中获取的,MmsConfig提供了很多Get方法以获取配置信息。
∙Presenter.java---用来展示附件的一个抽象类。
∙PresenterFactory.java---工厂方法。
∙RecipientsAdapter.java∙RecipientsEditor.java---用于显示信息编辑页面上面的收信人的编辑框,它可以有自动补全的功能,补全的数据由RecipientsAdapter来提供。
∙ViewInterface.java---代表一个View的基类,用于Slideshow显示内容或是取缩略图。
可以取View的长宽高等。
∙BasicSlideEditorView.java---编辑某一页幻灯片时所用的布局,也就是在SlideEditorActivity.java中使用。
∙EditSlideDurationActivity.java---顾名思义,用于编辑某一页幻灯片的浏览时长。
∙ManageSimMessages.java---这个是在设置中使用的,用来管理SIM里的消息。
在设置中有一项是管理SIM卡上面的消息。
在Mms的设置Settings中有一个选项可以设置是把信息存储在SIM 卡,还是存储在手机里。
在收信时SmsReceiverService会查看这个设置然后把收到的信息写到相应的地址。
ManageSimMessages也是以列表方式显示SIM里面的信息,提供了二个菜单:把信息存入手机和删除。
∙NumberPickerButton.java---用于显示选择数字的按扭,在配置里面用。
∙NumberPickerDialog.java---用于显示选择数字的对话框,在配置里面用。
∙NumberPicker.java---用于在配置的时候选择数字。
这几个NumerPicker主要是用于Settings中的。
∙DeliveryReportActivity.java---信息发送情况报告。
以列表的方式来显示∙DeliveryReportAdapter.java---相应的Adapter∙DeliveryReportItem.java---相应的数据,每一项的数据∙DeliveryReportListItem.java---相应每一项的布局。
data---用于操作当前正在编辑的信息的相关数据,比如联系人列表,比如当前对话,比如当前消息。
负责管理当前正在编辑的信息和当前所处的对话以及当前信息用到的联系人。
这些类都是在编辑信息的时候使用,由于这些多半都是用来管理数据的,而又无法直接做为对象传递给编辑器。
所以它们的很多方法都是静态的,也就是这些类都近似单键。
∙WorkingMessage.java---用来管理当前正在编辑的消息,它从创建,草稿到发送完成后一直存在,只要打开了编辑信息的页面就会创建一个WorkingMessage,直到退出编辑页面。
∙Conversation.java---用来管理对话Threads,通常用来管理当前的对话,也就是进入的对话和正在进行操作的对话,它也用来管理对话列表,比如查询对话列表。
∙Contact.java---用来代表一个联系人的信息,和管理联系人,加载联系人信息,其中还有相应的Cache。
因为一个联系人的数据是比较多的包含名字,名,姓,各种电话号码,各种地址等等。
因为Mms中直接使用Contact来作为联系人,所有信息都是直接从其中获取。
另外,由于信息交互中也会涉及到联系人,因为收发信时可以直接使用一串电话号码,这时就需要有如添加联系人的功能。
Contact中有很多异步的操作,比如加载联系人信息的时候或者更新Cache的时候都需要异步操作以不阻塞调用者。
∙ContactList.java---是一个Contact的List列表它继承自ArrayList<Contact>。