RilNET – Radio Interface Layer (RIL) .NET wrapper – sprostowanie
A propos artykułu RilNET – Radio Interface Layer (RIL) .NET wrapper należy się drobne sprostowanie działania biblioteki a przede wszystkim obsługi lokalizacji.
Otóż po wielu testach na telefonach firm HP i HTC oraz po przeczytaniu kilku artykułów w sieci wszystkim osobom, które używają RIL-a należy się małe wyjaśnienie o którym nie zdawałem sobie sprawy w momencie pisania wpisu.
Otóż implementacja biblioteki RIL zależy od producenta telefonu. Sprowadza się to do tego, iż część funkcji może nie być w ogóle zaimplementowana!
W praktyce wygląda to tak, iż mój wysłużony HP 614C nie posiadał zaimplementowanej metody udostępniającej informację o komórce sieci GetCellTowerInfo przez co w artykule błędnie zasugerowałem, że to pewnie wina sieci Orange.
Po zamianie i testach na HP Touch Pro2 wszystko działa poprawnie. Funkcja zwraca informacje o sieci!
Oczywiście można sobie zadać pytanie: no i co z tego? Otóż wbrew pozorom bardzo wiele. Chociażby to, że Google Maps potrafi obsługiwać RIL-a i jest w stanie określić waszą przybliżoną pozycję na podstawie wieży komórkowej.
Podsumowując. Jest to kolejny powód aby kupować telefony poważnych firm. Choć HP kiedyś bardzo się starało w sprawie PocketPC, obecnie daleko im do czołówki i standardów króla HTC.
RilNET – Radio Interface Layer (RIL) .NET wrapper
Długo się zanosiłem z wrapperem na RIL-a i zawsze mi brakowało czasu, ale może od początku…
Dla niezorientowanych Radio Interface Layer jest warstwą łączącą hardware telefonu z oprogramowaniem. Pisząc hardware mam na myśli część telefonu Windows Mobile odpowiedzialną za wykonywanie połączeń i transfer danych do stacji komórkowych (Radio). Z punktu widzenia programisty nic poniżej RIL-a już nie ma poza samym sprzętem. Więcej o samym systemie można poczytać na http://msdn.microsoft.com/en-us/library/aa920475.aspx.
Poniżej schemat architektury w Windows Mobile (RIL występuje także w Windows CE):

Jeżeli czytasz ten tekst pewnie zadasz pytanie “po co to komu?”. Z punktu widzenia CF mamy dostępne wrappery Microsoftu pozwalające na wykonywanie połączeń, wysyłanie SMS-ów, obsługę książki telefonicznej itp. Z punktu widzenia telefonu posiadamy jeszcze Tapi oraz ExTapi. A więc po co?
Otóż tak jak napisałem wcześniej RIL jest najniższą warstwą a więc pozwala na rzeczy na które wyższe warstwy nigdy nie zezwolą. Przy użyciu RIL-a możemy pobrać informację o wieży komórkowej, możemy zarządzać kartą SIM, możemy wysyłać komendy AT, itp.
RIL pozwala na dwa sposoby odwołań:
- Podłączenie się do systemu notyfikacji, przez co dostaniemy każde zdarzenie które wygeneruje Radio
- Wysyłanie zapytań do systemu, przez co możemy uzyskać dowolną informację z Radio
Poniżej prosty przykład użycia:
int hrCo, hrCti, hrEi;
int hr = Ril.Initialize(1, new RILRESULTCALLBACK(RilResultCallback), new RILNOTIFYCALLBACK(RilNotifyCallback), RIL_NCLASS.ALL, 0, out hRil);
// you can bind only to RilResultCallback
// int hr = Ril.Initialize(1, new RILRESULTCALLBACK(RilResultCallback), null, 0, 0, out hRil);
// or only to or RilNotifyCallback
// int hr = Ril.Initialize(1, null, new RILNOTIFYCALLBACK(RilNotifyCallback), RIL_NCLASS.ALL, 0, out hRil);
hrCti = Ril.GetCellTowerInfo(hRil);
hrCo = Ril.GetCurrentOperator(hRil, RIL_OPFORMAT.LONG);
hrEi = Ril.GetEquipmentInfo(hRil);
hr = Ril.Deinitialize(hRil);
// after call some method remember to save returned HRESULT and compare it to hrCmdID in RilResultCallback
private void RilResultCallback(
uint dwCode,
int hrCmdID,
IntPtr lpData,
uint cbData,
uint dwParam)
{
if (hrCo == hrCmdID)
{
//Ril.GetCurrentOperator
RILOPERATORNAMES pOperatorNames = (RILOPERATORNAMES)Marshal.PtrToStructure(lpData, typeof(RILOPERATORNAMES));
if ((pOperatorNames.dwParams & RIL_PARAM_ON.LONGNAME) == RIL_PARAM_ON.LONGNAME) // check that LongName member is valid
{
string longName = Encoding.ASCII.GetString(pOperatorNames.szLongName, 0, pOperatorNames.szLongName.Length).Replace("\0", "");
}
}
if (hrCti == hrCmdID)
{
//Ril.GetCellTowerInfo
RILCELLTOWERINFO pCellTowerInfo = (RILCELLTOWERINFO)Marshal.PtrToStructure(lpData, typeof(RILCELLTOWERINFO));
}
if (hrEi == hrCmdID)
{
//Ril.GetEquipmentInfo
RILEQUIPMENTINFO pEquipmentInfo = (RILEQUIPMENTINFO)Marshal.PtrToStructure(lpData, typeof(RILEQUIPMENTINFO));
if ((pEquipmentInfo.dwParams & RIL_PARAM_EI.MANUFACTURER) == RIL_PARAM_EI.MANUFACTURER)
{
string manufacturer = Encoding.ASCII.GetString(pEquipmentInfo.szManufacturer, 0, pEquipmentInfo.szManufacturer.Length).Replace("\0", "");
}
if ((pEquipmentInfo.dwParams & RIL_PARAM_EI.MODEL) == RIL_PARAM_EI.MODEL)
{
string model = Encoding.ASCII.GetString(pEquipmentInfo.szModel, 0, pEquipmentInfo.szModel.Length).Replace("\0", "");
}
if ((pEquipmentInfo.dwParams & RIL_PARAM_EI.REVISION) == RIL_PARAM_EI.REVISION)
{
string revision = Encoding.ASCII.GetString(pEquipmentInfo.szRevision, 0, pEquipmentInfo.szRevision.Length).Replace("\0", "");
}
if ((pEquipmentInfo.dwParams & RIL_PARAM_EI.SERIALNUMBER) == RIL_PARAM_EI.SERIALNUMBER)
{
string serialNumber = Encoding.ASCII.GetString(pEquipmentInfo.szSerialNumber, 0, pEquipmentInfo.szSerialNumber.Length).Replace("\0", "");
}
}
}
public void RilNotifyCallback(
uint dwCode,
IntPtr lpData,
uint cbData,
uint dwParam)
{
RIL_NCLASS dwClass = ((RIL_NCLASS)dwCode & RIL_NCLASS.ALL);
Debug.WriteLine("NotifyCallback: " + dwClass.ToString());
switch ((RIL_NOTIFY_RADIOSTATE)dwCode)
{
case RIL_NOTIFY_RADIOSTATE.RADIOEQUIPMENTSTATECHANGED:
{
RILEQUIPMENTSTATE pState = (RILEQUIPMENTSTATE)Marshal.PtrToStructure(lpData, typeof(RILEQUIPMENTSTATE));
Debug.WriteLine(String.Format("Radio Support: {0}; equipment State: {1}; ready State: {2}",
pState.dwRadioSupport, pState.dwEqState, pState.dwReadyState));
break;
}
case RIL_NOTIFY_RADIOSTATE.RADIOPRESENCECHANGED:
{
RIL_RADIOPRESENCE dwPresence = (RIL_RADIOPRESENCE)Marshal.ReadInt32(lpData);
switch (dwPresence)
{
case RIL_RADIOPRESENCE.NOTPRESENT:
Debug.WriteLine("Radio module is not present");
break;
case RIL_RADIOPRESENCE.PRESENT:
Debug.WriteLine("Radio module is present");
break;
}
break;
}
}
}
W przypadku gdy podłączamy się do notyfikacji musimy określić jakie notyfikacje nas interesują oraz musimy zdefiniować funkcję która będzie je odbierać. Po odebraniu zdarzenia musimy przy pomocy enumeratorów “rozwiązać” typ zdarzenia oraz zinterpretować dane. System jest tak zorganizowany, że z danymi przychodzi wskaźnik który w większości przypadków wskazuje na strukturę w której dodatkowo są zapisane informacje przy pomocy “flagowego” enumeratora o poprawności poszczególnych pól w tej strukturze.
W przypadku gdy wysyłamy zapytania po odwołaniu do funkcji musimy zapamiętać uchwyt, który funkcja zwraca a następnie w metodzie obsługującej musimy go porównać z uchwytem który uzyskujemy od urządzenia. Po tej operacji wskaźnik możemy skonwertować do odpowiedniego typu danych i podobnie jak poprzednio, odpowiednie pole wskazuje poprawność poszczególnych pól w strukturze.
Dla wszystkich osób zainteresowanych określaniem pozycji według stacji nadawczych mam złe wiadomości. Otóż w mojej sieci Orange, system zwraca błąd o braku wsparcia ze strony sieci. Może to wina miejsca w którym mieszkam a może tak jest wszędzie. Jak przetestuje to w kilku lokalizacjach to udostępnie podsumowanie.
Projekt dostępny jest oczywiście na CodePlex RilNET






