Get CellID, MCC, MNC, LAC, and Network in iOS 5.1
Asked Answered
W

3

18

I need to retrieve CellID, MCC, MNC, LAC and Network (GSM, 3G) of the current Serving Cell Tower in iOS 5.1 (iPhone 4S). I know this information is available because I can see it in FieldTest Mode (accessible after calling ****3001#12345#****). I suppose it to be accessible via Private/Undocumented iOS Frameworks.

In the question iphone, check values of cellId / Lac the author indicates I can get radio Informations cellId, Lac, MNC, MCC on iOS, but no information on how to do this is provided.

Can anybody tell me how to get this info?

Willpower answered 15/11, 2012 at 14:33 Comment(14)
for MNC and MCC: #678985Lamb
I have tried this to get MCC and MNC, but I get the following runtime error: -[CTTelephonyNetworkInfo suscriberCellularProvider]: unrecognized selector sent to instance 0x226550 Reason?Willpower
Did you include CoreTelephony.framework in your project and import it's headers?Lamb
Yes I did. I added the lines #import <CoreTelephony/CTTelephonyNetworkInfo.h> and #import <CoreTelephony/CTCarrier.h> and I compiled after including CoreTelephony.frameworkWillpower
I see: you're trying to use a class as an object. You have to create an instance of CTTelephonyNetworkInfo - just like the accepted answer to linked question above.Lamb
I got it! It was just a typo error... I was calling suscriberCellularProvider instead of subscriberCellularProvider Now it works for MNC and MCC... Now I need CellID and LAC, what I suppose to be difficult...Willpower
Stumbler - which is great for WiFi - also has a module called CellStumbler. I didn't test it but it looks this would be the place to start. This project uses private API. And I think you can't get CellID or LAC on iPhone without private API. Definetly not in case when positioing is turned off.Lamb
Well.. I knew Stumbler... For Wifi it still works on iOS5.1 (after considering the minimal changes indicated here: blog.guvenergokce.com/iphone-wireless-scanner-ios5/170). However, CellStumbler (which makes extensive use of private APIs) does not work on iOS5.1. The old private APIs have perhaps been modified, which is the reason of my question... @nico24 told that he was able to get the CellID and LAC info I need (his question was made on Aug20, 2012 so I suppose he was using iOS5) so perhaps he knows the way to do it by using the new private APIs...Willpower
I see. I left him (nico24) a note (since you need 50rep to do it) - hope he sees it and helps you with this - it's really an interesting topic.Lamb
After all this time of googling and research, I cannot find the solution... I have been trying dissasembling FieldTest with IDA Pro, loading private GMM and CoreTelephony frameworks, etc., but nothing... GMM framework has anything to manage LAC and CellID, but I don't know how to initialize the values I need... @rokjarc, I didn't receive any response from (nico24), anyway, thanks for your support, it is very appreciated!Willpower
@rokjarc Are you able to get Cell Id and LAC? If so can you suggest me how to proceed.Hogg
@Dee: i don't know how to get this information on iOS devices. User nico24 is supposed to know how to do this it seems he is not active on SO anymore.Lamb
@rokjarc Yes I observed.Hogg
@Willpower Are you able to get Cell Id and RxValues?Hogg
S
18

I know three ways on how you can do it on iOS 5.x - 7.x. All of them use private APIs from CoreTelephony.framework. Supports both GSM and UMTS.

1) Using cell monitor

struct CTResult
{
    int flag;
    int a;
};

extern CFStringRef const kCTCellMonitorCellType;
extern CFStringRef const kCTCellMonitorCellTypeServing;
extern CFStringRef const kCTCellMonitorCellTypeNeighbor;
extern CFStringRef const kCTCellMonitorCellId;
extern CFStringRef const kCTCellMonitorLAC;
extern CFStringRef const kCTCellMonitorMCC;
extern CFStringRef const kCTCellMonitorMNC;
extern CFStringRef const kCTCellMonitorUpdateNotification;

id _CTServerConnectionCreate(CFAllocatorRef, void*, int*);
void _CTServerConnectionAddToRunLoop(id, CFRunLoopRef, CFStringRef);

#ifdef __LP64__

void _CTServerConnectionRegisterForNotification(id, CFStringRef);
void _CTServerConnectionCellMonitorStart(id);
void _CTServerConnectionCellMonitorStop(id);
void _CTServerConnectionCellMonitorCopyCellInfo(id, void*, CFArrayRef*);

#else

void _CTServerConnectionRegisterForNotification(struct CTResult*, id, CFStringRef);
#define _CTServerConnectionRegisterForNotification(connection, notification) { struct CTResult res; _CTServerConnectionRegisterForNotification(&res, connection, notification); }

void _CTServerConnectionCellMonitorStart(struct CTResult*, id);
#define _CTServerConnectionCellMonitorStart(connection) { struct CTResult res; _CTServerConnectionCellMonitorStart(&res, connection); }

void _CTServerConnectionCellMonitorStop(struct CTResult*, id);
#define _CTServerConnectionCellMonitorStop(connection) { struct CTResult res; _CTServerConnectionCellMonitorStop(&res, connection); }

void _CTServerConnectionCellMonitorCopyCellInfo(struct CTResult*, id, void*, CFArrayRef*);
#define _CTServerConnectionCellMonitorCopyCellInfo(connection, tmp, cells) { struct CTResult res; _CTServerConnectionCellMonitorCopyCellInfo(&res, connection, tmp, cells); }

#endif

...

id CTConnection = _CTServerConnectionCreate(NULL, CellMonitorCallback, NULL);
_CTServerConnectionAddToRunLoop(CTConnection, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
_CTServerConnectionRegisterForNotification(CTConnection, kCTCellMonitorUpdateNotification);
_CTServerConnectionCellMonitorStart(CTConnection);

int CellMonitorCallback(id connection, CFStringRef string, CFDictionaryRef dictionary, void *data)
{
    int tmp = 0;
    CFArrayRef cells = NULL;
    _CTServerConnectionCellMonitorCopyCellInfo(connection, (void*)&tmp, &cells);
    if (cells == NULL)
    {
        return 0;
    }

    for (NSDictionary* cell in (NSArray*)cells)
    {
        int LAC, CID, MCC, MNC;

        if ([cell[(NSString*)kCTCellMonitorCellType] isEqualToString:(NSString*)kCTCellMonitorCellTypeServing])
        {
            LAC = [cell[(NSString*)kCTCellMonitorLAC] intValue];
            CID = [cell[(NSString*)kCTCellMonitorCellId] intValue];
            MCC = [cell[(NSString*)kCTCellMonitorMCC] intValue];
            MNC = [cell[(NSString*)kCTCellMonitorMNC] intValue];
        }
        else if ([cell[(NSString*)kCTCellMonitorCellType] isEqualToString:(NSString*)kCTCellMonitorCellTypeNeighbor])
        {
        }
    }

    CFRelease(cells);

    return 0;
}

2) Using CTTelephonyCenter

kCTRegistrationCellChangedNotification is sent every time current serving cell tower is changed.

extern CFStringRef const kCTRegistrationCellChangedNotification;
extern CFStringRef const kCTRegistrationGsmLac;
extern CFStringRef const kCTRegistrationLac;
extern CFStringRef const kCTRegistrationGsmCellId;
extern CFStringRef const kCTRegistrationCellId;

CFStringRef CTSIMSupportCopyMobileSubscriberCountryCode(CFAllocatorRef);
CFStringRef CTSIMSupportCopyMobileSubscriberNetworkCode(CFAllocatorRef);

id CTTelephonyCenterGetDefault();
void CTTelephonyCenterAddObserver(id, void, CFNotificationCallback, CFStringRef, void, CFNotificationSuspensionBehavior);

...

CTTelephonyCenterAddObserver(CTTelephonyCenterGetDefault(), NULL, callback, NULL, NULL, CFNotificationSuspensionBehaviorHold);

void callback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
    NSString* notification = (NSString*)name;
    NSDictionary *cellInfo = (NSDictionary*)userInfo;

    if ([notification isEqualToString:(NSString*)kCTRegistrationCellChangedNotification])
    {
        int LAC, CID, MCC, MNC;

        if (cellInfo[(NSString*)kCTRegistrationGsmLac])
        {
            LAC = [cellInfo[(NSString*)kCTRegistrationGsmLac] intValue];
        }
        else if (data[(NSString*)kCTRegistrationLac])
        {
            LAC = [cellInfo[(NSString*)kCTRegistrationLac] intValue];
        }

        if (cellInfo[(NSString*)kCTRegistrationGsmCellId])
        {
            CID = [cellInfo[(NSString*)kCTRegistrationGsmCellId] intValue];
        }
        else if (cellInfo[(NSString*)kCTRegistrationCellId])
        {
            CID = [cellInfo[(NSString*)kCTRegistrationCellId] intValue];
        }

        MCC = [[(NSString*)CTSIMSupportCopyMobileSubscriberCountryCode(NULL) autorelease] intValue];
        MNC = [[(NSString*)CTSIMSupportCopyMobileSubscriberNetworkCode(NULL) autorelease] intValue];
    }
}

3) This returns current serving cell tower

struct CTResult
{
    int flag;
    int a;
};

id _CTServerConnectionCreate(CFAllocatorRef, void*, int*);

#ifdef __LP64__

void _CTServerConnectionGetLocationAreaCode(id, int*);
void _CTServerConnectionGetCellID(id, int*);

#else

void _CTServerConnectionGetLocationAreaCode(struct CTResult*, id, int*);
#define _CTServerConnectionGetLocationAreaCode(connection, LAC) { struct CTResult res; _CTServerConnectionGetLocationAreaCode(&res, connection, LAC); }

void _CTServerConnectionGetCellID(struct CTResult*, id, int*);
#define _CTServerConnectionGetCellID(connection, CID) { struct CTResult res; _CTServerConnectionGetCellID(&res, connection, CID); }

#endif

...

int CID, LAC, MCC, MNC;

id CTConnection = _CTServerConnectionCreate(NULL, NULL, NULL);
_CTServerConnectionGetCellID(CTConnection, &CID);
_CTServerConnectionGetLocationAreaCode(CTConnection, &LAC);
MCC = [[(NSString*)CTSIMSupportCopyMobileSubscriberCountryCode(NULL) autorelease] intValue];
MNC = [[(NSString*)CTSIMSupportCopyMobileSubscriberNetworkCode(NULL) autorelease] intValue];

UPDATE

On ARM64 (iPhone 5S) there is an issue with all CoreTelephony functions that accept struct CTResult argument. Apparently, 64-bit version of CoreTelephony exports these functions without struct CTResult argument. Because of that you will get an error on ARM64 if you call these functions like you did in the past - arguments will be wrong. I updated function declarations so that they work on both 32-bit and 64-bit ARM architectures. I tested it and it works on both iPhone 4S and iPhone 5S.

This only applies to ARM64. If you build your project for 32-bit ARM architecture then there is no such issue. Your application will use 32-bit version of CoreTelephony which expects struct CTResult argument.

8.3 UPDATE

As of iOS 8.3 all of the above solutions require entitlement to work

<key>com.apple.CommCenter.fine-grained</key>
<array>
    <string>spi</string>
</array>

Not only cell monitor is protected but it seems like all of the CoreTelephony notifications now require that entitlement to work. For example, kCTMessageReceivedNotification also affected.

Stitt answered 16/10, 2013 at 10:3 Comment(17)
And this is still possible with iOS 7 to get the Cell ID?!Hilbert
Yes, it tested on iOS7.Stitt
How did u manage it to work?! Do i have to import the private classes?!Hilbert
Those are private APIs so you need to declare all function yourself. I provided all declarations you need in the answer. For me, cell monitor is the best way of all three. Gives you much more information.Stitt
What exactly do you mean by declare it by yourself?!Hilbert
let us continue this discussion in chatHilbert
Can you come to chat please?!Hilbert
That works perfect for GSM, but it crashes when in 3G or 4G...You have a Solution?Hilbert
I tested it for both GSM and UMTS. No crashes. Don't know about LTE.Stitt
Yes, my fault! It crashes on iPhone 5s...Has nothing to do with 3G or 2G...I tested on both 5s and 5...On 5s it crashes when register the _CTServerConnectionRegisterForNotification(&res, CTConnection, kCTCellMonitorUpdateNotification);Hilbert
@chrizstone, I updated my answer. Now works on ARM64.Stitt
That works perfect...Thx for your work...But i think in 3G the Cell ID is some how Encrypted....Hilbert
I have tried this and works perfectly,Thanks! The thing is: I get the same cellid for kCTCellMonitorCellTypeServing and kCTCellMonitorCellTypeNeighbor, does anyone know hot to solve this? Please helpLockyer
I have problems implementing this code. Don't know what goes in the .h file and what goes in the .m Could you upload the separated classes or maybe upload a project to see the implementation?Hunch
@creker- when using first method. i am getting use of undeclared "CellMonitorCallback"Truthfunction
Have any clue to fix on ios 8.3 ?Gwendolin
@kml_ckr, posted the solution. Now it requires jailbreak to workStitt
A
2

suscriberCellularProvider is an object method (vs class method).

You can take a look how to use it here: Determine iPhone user's country

I think CTCarrier has MCC and MNC.

You can check network type using the code from this question: How to check if iPhone supports CDMA or GSM

And look this question for CellID: CTServerConnectionGetCellID routine core telephony

Acetal answered 15/11, 2012 at 16:3 Comment(0)
H
0

The code below is how to insert to che entitlement to meke the code work on ios 8.3. As of iOS 8.3 all of the above solutions require entitlement to work

<key>com.apple.CommCenter.fine-grained</key>
<array>
    <string>spi</string>
</array>

Indeed, tha above code mentioned is said that can be run to get the lac and cell on ios 8.3 and above. But I really don't know how to insert the above on a jailbroken phone. Could anyone give any detail information.

Haberdasher answered 19/10, 2015 at 3:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.