前段时间,为了实现WM手机的一些底层功能,需要对RIL的一些函数进行调用,这个过程中发现RILNOTIFYCALLBACK表现得不尽如意:在模拟器上实现的Notification注册在手机上却没有成功,因为缺少文档也没有找到原因。结果实现的功能主要是基于RILResultCallback的。当时为了提高效率,对RIL进行了封装,最近觉得这个封装应该进一步修改,想想自己的Blog还没有什么干货,干脆就先把以前实现的在这里总结一下,虽然因为工作的关系不能公布绝大多数代码,但也许我的思路和方法能对别人有所裨益。
大体来说,最直接地实现RIL Result Callback需要以下几步(可参见http://bbs.cnw.com.cn/viewthread.php?tid=134138):
1)定义相应的数据结构,例如RILCELLTOWERINFO,需要区别C++中数据类型与CTS类型的转换;
2)定义函数原型(包括RIL_Initialize、RIL_Deinitialize、RILRESULTCALLBACK, RILNOTIFYCALLBACK);
3)定义具体调用的RIL Native函数原型,例如RIL_GetCellTowerInfo,这个函数的调用需要RIL_Initialize成功初始化后抓到的Handle;
4)需要定义一个符合RILRESULTCALLBACK签名的回调函数,比如我定义的OnResultCallback,来处理调用RIL_GetCellTowerInfo后返回的内容;
到这里其实都是些预备工作,真正的调用过程非常格式化:
5)执行初始化,主要是RIL_Initialize获得一个执行后续函数的Handle,同时设定处理回调数据的函数(OnResultCallback),这里out hRes指示初始化是否成功;
6)以这个Handle为参数,执行具体的Native函数,比如这里的RIL_GetCellTowerInfo;
7)执行RIL_Deinitialize并释放5)得到的Handle,当然为避免锁死这里应该用AutoResetEvent。
总体来说,调用不同RIL ResultCallback函数的过程基本都是5)、6)、7)这三步,差别在于6)用到的RIL函数,以及5)设定的回调函数这两块,很自然的我就考虑能否定义一个泛型方法来实现不同RIL函数的调用。
经过考虑,发现RIL定义的绝大多数数据结构都有非常类似的框架和处理模式,特别是大多数结构的头两项分别是dwSize(有时也叫cbSize)和dwParams,分别存储结构体的长度并指示后续字段是否有效(dwParams其实就是一个基于bit的Flag,bit0指示之后第一个字段的有效性,bit1指示之后第二个。。。)。不过之后的字段比较麻烦,可能有几种不同的类型,主要分别对应CTS的UInt32、Int32、byte[]、String,也有一些可能是枚举或者嵌套的RIL结构体。由于工作初期我关注的是有效显示这些内容,所以在基类里实现ToString就足够了。这样一来,就可以定义一个基类,通过反射机制提取继承类的有效信息,代码如下就不赘述了。
#region GetCellTowerInfo()
[StructLayout(LayoutKind.Sequential)]
public abstract class RilResultStructure
{
public override string ToString()
{
StringBuilder sb = new StringBuilder(500);
Type theRilType = this.GetType();
MemberInfo[] members = // will return public, non-static, and non-inherited members.
theRilType.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
sb.Append(theRilType.Name + "\r\n");
sb.Append("======================\r\n");
uint mask = uint.MaxValue;
int maskPos = 0;
bool isValid;
FieldInfo fi;
for (int i = 0; i < members.Length; i++)
{
MemberTypes memberType = members[i].MemberType;
if (memberType == MemberTypes.Field)
{
fi = members[i] as FieldInfo;
switch (members[i].Name)
{
case "dwSize":
case "cbSize":
sb.Append(fi.Name + " = " + (uint)fi.GetValue(this) + "\r\n");
continue;
case "dwParams":
mask = (uint)(fi.GetValue(this));
maskPos = i;
string binStr = Convert.ToString(mask, 2);
//string binStr = Convert.ToString(mask, 2).Trim(new char[] { '0' });
int count = System.Text.RegularExpressions.Regex.Matches(binStr, "1").Count;
sb.Append("dwParams = 0x" + mask.ToString("X2"));
sb.Append("\t (" + count + " valid values)\r\n");
continue;
default:
isValid = (mask == uint.MaxValue) ? true : ((mask >> (i - 1 - maskPos)) & 0x01) != 0;
if (isValid)
{
string typeName = fi.FieldType.Name;
sb.Append(fi.Name + " = ");
switch (typeName)
{
case "UInt32":
uint var = (uint)fi.GetValue(this);
sb.Append(var.ToString());
sb.Append(" [0x" + Convert.ToString(var, 16) + "]\r\n");
break;
case "Int32":
int vari = (int)fi.GetValue(this);
sb.Append(vari.ToString() + "\r\n");
break;
case "Byte[]":
byte[] bytes = (byte[])(fi.GetValue(this));
if (bytes != null)
{
int length = Array.IndexOf<byte>(bytes, 0);
//byte[] unicodeBytes = Encoding.Convert(Encoding.ASCII, Encoding.Unicode, bytes, 0, length);
//char[] chars = Encoding.Unicode.GetChars(unicodeBytes);
char[] chars = Encoding.Default.GetChars(bytes, 0, length);
string content = new string(chars);
sb.Append(content + "\r\n");
}
break;
case "String":
sb.Append((string)(fi.GetValue(this)) + "\r\n");
break;
default:
if (fi.FieldType.IsEnum)
{
sb.Append((fi.GetValue(this)).ToString() + "\r\n");
}
//else if (fi.FieldType.IsArray)
//{
// object arrays = fi.GetValue(this);
//}
else if (fi.FieldType.IsNestedPublic)
{
if (fi.FieldType.IsClass)
{
Debug.WriteLine("The nested object shall be struct instead of class: " + fi.FieldType.Name);
break;
}
sb.Append("\r\n" + fi.GetValue(this).ToString());
}
else
{
sb.Append(fi.FieldType.ToString() + "\r\n");
}
break;
}
}
break;
}
}
}
return sb.ToString();
}
}
#endregion
这样一来,不同的Ril结构定义就极大地简化了,以RILCELLTOWERINFO为例,只需要继承自RilResultStructure就OK了。
#region RIL Cell Tower Info
// refer to http://msdn.microsoft.com/en-us/library/aa921533.aspx
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class RILCELLTOWERINFO : RilResultStructure
{
public uint cbSize;
public uint dwParams;
public uint dwMobileCountryCode;
public uint dwMobileNetworkCode;
public uint dwLocationAreaCode;
public uint dwCellID;
public uint dwBaseStationID;
public uint dwBroadcastControlChannel;
public uint dwRxLevel;
public uint dwRxLevelFull;
public uint dwRxLevelSub;
public uint dwRxQuality;
public uint dwRxQualityFull;
public uint dwRxQualitySub;
public uint dwIdleTimeSlot;
public uint dwTimingAdvance;
public uint dwGPRSCellID;
public uint dwGPRSBaseStationID;
public uint dwNumBCCH;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = RIL.MAXLENGTH_BCCH)]
public Byte[] rgbBCCH;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = RIL.MAXLENGTH_NMR)]
public Byte[] rgbNMR;
}
#endregion
由于返回数据继承于同一个基类,实际上回调函数OnResultCallback也就实现基类提供的ToString,就可以把派生类的内容正确显示。于是,我所期望的泛型函数其实只需要一个指向具体RIL Native函数的指针就够了。不过真正实现时,为了表面回调函数处理后的数据结构类型,还是需要提供一个new派生类,存储结果的同时指示OnResultCallback中lpData引用数据的结构。
最终这样一个泛型就实现了5)、6)、7)这三步:
public static void GenericRilResult<T>(T result, RilResultFuncDelegate rilFuncDel) where T : RilResultStructure, new()
{
// Invoke the RIL function stored in rilFunc Delegate
if (rilFuncDel == null || result == null)
{
Debug.WriteLine("Parameter Error.");
return;
}
if (initialize(result))
{
IntPtr hRes = rilFuncDel(hRilResult);
deinitialize();
}
return;
}
其中RilResultFuncDelegate 是对绝大多数RIL函数签名的定义:
public delegate IntPtr RilResultFuncDelegate(IntPtr hRil); //hRil: Handle to the RIL instance returned by RIL_Initialize.
调用不同RIL函数也就因此能够实现标准化(其中rilResult是一个全局object):
public static RILCELLTOWERINFO GetCellTowerInfo()
{
GenericRilResult(new RILCELLTOWERINFO(), new RilResultFuncDelegate(RIL_GetCellTowerInfo));
return rilResult as RILCELLTOWERINFO;
}
#endregion
要正确显示结果的内容就可以这样:
textBox1.Text = RIL.GetCellTowerInfo().ToString();
至于有些拥有不同签名的RIL函数,例如RIL_GetCurrentOperator,也可以定义一个类似的泛型函数,大体就差不多了。
这么做的结果就是要支持新的RIL函数,基本只需要定义相应的派生于RilResultStructure的数据结构和Native函数原型,然后调用泛型就可以了。