Android - (ID) __Cellular Internals Telephony


Android is a System that is built around services like most Systems. A services Purpose to put simply is to well “service” something to someone.
It just gives benefits to the Systems so they can Manage Data for purposes such as Security to ensure Permissions via Callers, or in general maintenance or Supply the Service.
Your few lines of a code as a Dev seem like a bless but the code to create that function most likely lies hundreds of other lines of code to make the function work.
Telephony
Android is well a Telephone as in it allows you to communicate to other people around the world using Cell Towers, GSM/CDMA Networks etc.
The Packages that typically manage or in this case “service” Telephony Services are:
com.android.phone (Phone Services)
com.android.providers.telephony (Phone and Messaging Storage)
Phone Services will act as the Communication between the Storage Provider Service and the Provider will manage the Database / Files aka Storage.
You in theory use a Combination of Call and Query Content Resolver Requests to Communicate to the Storage Provider and the Service would most likely be done more directly with Binder.
lets first find what we can via command line, shell. Now some of these commands will require elevated privileges / Root.
vermeer:/ # service list | grep -iE 'telephon|phone'
95 carrier_config: [com.android.internal.telephony.ICarrierConfigLoader]
130 extphone: [org.codeaurora.internal.IExtTelephony]
143 imms: [com.android.internal.telephony.IMms]
151 ions: [com.android.internal.telephony.IOns]
152 iphonesubinfo: [com.android.internal.telephony.IPhoneSubInfo]
154 isms: [com.android.internal.telephony.ISms]
155 isub: [com.android.internal.telephony.ISub]
202 miui.radio.extphone: [miui.telephony.IMiuiTelephony]
240 phone: [com.android.internal.telephony.ITelephony]
250 qti.radio.extphone: [org.codeaurora.internal.IExtTelephony]
275 simphonebook: [com.android.internal.telephony.IIccPhoneBook]
297 telephony.registry: [com.android.internal.telephony.ITelephonyRegistry]
298 telephony_ims: [android.telephony.ims.aidl.IImsRcsController]
We will look at the ‘iphonesubinfo’, ‘isub’, ‘phone’ Descriptors / their Service.
You can communicate to something like ‘iphonesubinfo’ ‘com.android.internal.telephony.IPhoneSubInfo’ via shell using the “service” command we were using with the Argument “call” passing the name of the Descriptor.
In earlier versions of Android you can through elevated privileges for most Command for ‘iphonesubinfo’ you can get the IMEI. Later versions of Android you must use your Dialler \#06#* .
Lets do a dumpsys on iphonesubinfo:
dumpsys iphonesubinfo # Gives us nothing
dumpsys | grep -i 'iphonesubinfo' # Gives us alot, but some bits feels missing so lets add
dumpsys | grep -i -A10 -B10 'iphonesubinfo' # Gives us the parts of text around it that it was found in
-A10 -B10 ensures that the Grep output will Contain the Contents that surrounds that String that was found. So if your target string example was found in a Stack Trace, it will instead of showing that one stack trace element show the Full trace that String was found in.
Executing a ‘dumpsys’ on the service ‘iphonesubinfo’ returns nothing due to the service lacking the implementation of a ‘dump’ method. ‘dumpsys’ will look for this method within the Service assuming you are dumping a service, and its output from the implemented ‘dump’ function will be what is shown. Once again in this case it lacks that method, so we have to use a combination of already done research, and source code to guess.
Typically when the Method signature that the Service implements to create a ‘dump’ looks like
/**
* Dumps the state of this service for debugging purposes.
*
* This method is called by the system when a dumpsys command is executed
* to provide diagnostic information about the service.
*
* @param fd The file descriptor to which the dump should be written.
* This is typically used for redirecting output to a file.
*
* @param writer A PrintWriter instance that provides convenient methods
* for writing formatted text to the output. Services should
* use this parameter rather than writing directly to the fd.
*
* @param args Command-line arguments passed to the dumpsys command.
* These can be used to customize what information is dumped
* (e.g., "--help", "--all", or specific subsystem names).
* If no arguments are provided, the service should dump all
* relevant state information.
*/
public void dump(FileDescriptor fd, PrintWriter writer, String[] args);
We will look into more examples of this method being implemented and used etc later when we go to reverse engineer the actual services source code.
Other commands seems to give us a lot of info such as other Interfaces, Packages, Logs, Cell Information etc. None of these seems to provide a list of available Command Numbers to execute for the Interface iphonesubinfo.
Doing some Research, for the interface ‘com.android.internal.telephony.IPhoneSubInfo’ gives us some code snippets of the AIDL definition or even source. Either one is describing the layout of the descriptor at least what functions lay within it. Now the AIDL interface can describe the numbers based off of the functions order starting the top can be command 1, least how it appears for me in Android Studio by default.
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
https://github.com/ChameleonOS/miui_framework/blob/master/java/com/android/internal/telephony/IPhoneSubInfo.java
I’m not sure if you can apply that same logic in this case with code provided from the source, but there are some results either way for the ‘iphoneusbinfo’ call numbers.
1 getDeviceId
2 getDeviceIdForSubscriber
3 getImeiForSubscriber
4 getDeviceSvn
5 getSubscriberId
6 getSubscriberIdForSubscriber
7 getGroupIdLevel1
8 getGroupIdLevel1ForSubscriber
9 getIccSerialNumber
10 getIccSerialNumberForSubscriber
11 getLine1Number
12 getLine1NumberForSubscriber
13 getLine1AlphaTag
14 getLine1AlphaTagForSubscriber
15 getMsisdn
16 getMsisdnForSubscriber
17 getVoiceMailNumber
18 getVoiceMailNumberForSubscriber
19 getCompleteVoiceMailNumber
20 getCompleteVoiceMailNumberForSubscriber
21 getVoiceMailAlphaTag
22 getVoiceMailAlphaTagForSubscriber
23 getIsimImpi
24 getIsimDomain
25 getIsimImpu
26 getIsimIst
27 getIsimPcscf
28 getIsimChallengeResponse
29 getIccSimChallengeResponse
We got a lot, and we now can start testing some of these via Command Line/ Shell.
As far as the use goes for the Code pages is, that I can say it should reflect the arguments that will be need to be passed to complete the Service Call for the number.
Lets try some of these, with an inserted SIM Card, we will try to get the phone number. Now some of these Require either elevated Permissions and or are Locked out of usage such as the IMEI Ones most likely. As far as what ones may require elevated privileges such as root is research for another time. You can see from the Source of the AIDL Interface attributes above some Functions Describing if it has been Deprecated, Requires certain Permissions what not.
Lets try get our Number, seems that “getLine1Number” may be our target Service Call with the Number of [11]. We can also see further Details about this interface including this function, since Function names on the “AIDL” interface pretty much reflect actual Interfaces/ Classes from the Android Framework counter part. Service and the Client app share the Same Layout of the Interface, Only one is to be a map to Communicate to a Interface while the other size in theory would hold Same layout but with code filling up the functions giving them empty seeming functions a purpose.
https://developer.android.com/reference/android/telephony/TelephonyManager#getLine1Number()
You as the client just has to bind the Interface you want to Connect to, using either a pre set a knowledge of Command numbers and what to pass via Parcels (Data and Reply Parcels) then Transact to the Service. Else you can use essentially a map of the exposing Functions to the Interface, hence the existence of the Class in the Framework ‘android.telephony.TelephonyManager’.
in this case “getLine1Number” seems to be deprecated in API level 33 aka Android (13) (Tiramisu)
service call SERVICE_NAME COMMAND_NUMBER ARGS...
service call iphonesubinfo 11 # We have Android 13 so of course this does nothing its deprecated
service call iphonesubinfo 1 # Pretty much gives us a empty reply Parcel (getDeviceId/imei/meid)
service call iphonesubinfo 15 # Returns (1.4.8.5.8.5.5.6.9.4.1)
service call iphonesubinfo 26 # Parcel(
0x00000000: 00000000 0000000a 006f0056 00630069 '........V.o.i.c.'
0x00000010: 00200065 0061004d 006c0069 00000000 'e. .M.a.i.l.....')
The Reply parcel with this Interface is the parcel we will be looking at to get our return value.
How the higher level communication happens (at least when compared to direct IOCTL) is mostly done through Parcel, Data parcel being Data you can give it or get in from a Call-back, and a Reply parcel that is used similar to like a return.
The data returned or replied back seems to be separated with periods indicating most likely it is a (2) Byte character encoded string (Unicode/UTF16) as since this string can be represented with a (1) Byte Char Style and is perhaps read as so, so the second byte in the (2) Char usually being (00) since again the chars fall within a small range. 00 being represented as the (00) char
You can confirm this by looking at the Hex Bytes that is shown on the reply Data next to the String.
Now it seems to me the Service Call Number (15) that is said to be (getMsisdn) seems to not be correct, possibly a Service call for the phone Number ?
(19) Also seems to Produce the same results, (26) Returns a String that says “Voice Mail”, and Number (12) returns that seems to be the ICC ID. So most likely in this case (12) is most likely function (getIccSerialNumber).
Talking to A.I it has aggregated list of what it believes are the names and the numbers to the calls:
+----+-----------------------------------------+--------------------------------+
| Idx| Method Name | Parameters |
+----+-----------------------------------------+--------------------------------+
| 1 | getDeviceId | None |
| 2 | getDeviceIdForSubscriber | int subId |
| 3 | getImeiForSubscriber | int subId |
| 4 | getDeviceSvn | None |
| 5 | getSubscriberId | None |
| 6 | getSubscriberIdForSubscriber | int subId |
| 7 | getGroupIdLevel1 | None |
| 8 | getGroupIdLevel1ForSubscriber | int subId |
| 9 | getIccSerialNumber | None |
| 10 | getIccSerialNumberForSubscriber | int subId |
| 11 | getLine1Number | None |
| 12 | getLine1NumberForSubscriber | int subId |
| 13 | getLine1AlphaTag | None |
| 14 | getLine1AlphaTagForSubscriber | int subId |
| 15 | getMsisdn | None |
| 16 | getMsisdnForSubscriber | int subId |
| 17 | getVoiceMailNumber | None |
| 18 | getVoiceMailNumberForSubscriber | int subId |
| 19 | getCompleteVoiceMailNumber | None |
| 20 | getCompleteVoiceMailNumberForSubscriber | int subId |
| 21 | getVoiceMailAlphaTag | None |
| 22 | getVoiceMailAlphaTagForSubscriber | int subId |
| 23 | getIsimImpi | None |
| 24 | getIsimDomain | None |
| 25 | getIsimImpu | None |
| 26 | getIsimIst | None |
| 27 | getIsimPcscf | None |
| 28 | getIsimChallengeResponse | in byte[] challenge |
| 29 | getIccSimChallengeResponse | int subId, in byte[] challenge |
| 30 | getNai | None |
| 31 | getNaiForSubscriber | int subId |
| 32 | getDeviceIdWithFeature | None |
| 33 | getDeviceIdWithFeatureForSubscriber | int subId, in String featureId |
+----+-----------------------------------------+--------------------------------+
So we can mess with the Service in this way , demonstrating a interesting way to communicate to the Service. Now these again can vary as in my case it seems (12) is (getIccSerialNumber) so take these with a grain of salt but more of an understanding of what can exist.
Looking into a Project Called “android-svc” we can see how they get a list of the Function Names including numbers. The numbers are just from first function in the AIDL file and increment from there. Lets move on, I will not get into how to Install this Script or how it works in depth, good luck have fun but I found my “AIDL”.
In ‘IPhoneSubInfo.aidl’ we see the (15th) function is “getLine1Number“. This makes sense as when we invoke that Service call we get a Phone number result.
Java TelephonyManager
Lets move on, lets now look at this from a Programmatic way, lets first look into a common use of getting the IMEI. IMEI in this case would relate to the card being (GSM) while MEID being (CDMA) based networks.
Now in later versions of Android (10+) will no longer allow users to get this ID as stated before you must use the Dialler.
“Starting in Android 10, apps must have the READ_PRIVILEGED_PHONE_STATE privileged permission in order to access the device's non-resettable identifiers, which include both IMEI and serial number.”
TelephonyManager telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.getDeviceId();
That’s all, mainly two lines of Code, one to get the Service "TELEPHONY_SERVICE” and then call to the function “getDeviceId” in theory you get the IMEI/MEID.
Behind that though lays layers of code that utilizes the “android.os.Binder” to help bind and Communicate the Telephony Services, with a target Descriptor.
public String getDeviceId() throws RemoteException {
String descriptor = "com.android.internal.telephony.IPhoneSubInfo";
Parcel data = Parcel.obtain(); // Create a Empty Parcel for the Data going to the Service
Parcel reply = Parcel.obtain(); // Create a Empty Parcel for the Reply coming back from the Command
data.writeInterfaceToken(descriptor); // Write the Destination of this Data, sepcify Interface Name
mRemote.transact(1, data, reply, 0); // Execute Command (1) giving Data Parcel holding the Destination Descriptor, Reply for the Result, (0) for the Flags
reply.readException(); // Read Exception, typically indication flag if exception has occurred
String id = reply.readString(); // Read the String in the Reply, this String will be the Result of the Command being IMEI/MEID
reply.recycle(); // Clean up the Reply Parcel
data.recycle(); // Clean up the Data Parcel
return id; // Return the Read IMEI/MEID Result
}
To get more information on the SIM Card, we represent the object as a “android.telephony.SubscriptionInfo” object. Subscription being that you subscribed to a MOBILE Network, it can be through SIM Card or E SIM.
So in this case to get other SIM Card objects, and or a list of all the SIMs we can use the function. When we Get a list of the Active Sim cards as Androids can have Up to (2) Sim Cards inserted into the Device at once. Each inserted SIM Card would be considered an active Subscription, the way to identify the Subscription (SIM Card) you want to do actions to is via Index. So if the SIM you want to get the MNC or MCC Value for is inserted into SIM Slot 1 then (0) would be the Subscription ID you want to do actions too, (0) because in programming in most languages indexes start on (0).
Some of the exposing functions on the “TelephonyManager” class provided by Android Framework get their Data from “build.prop” properties. Such as the Function “getSimOperatorName"
Looks something like this:
getSimOperatorName()
└─> getSimOperatorName(subId)
└─> getSimOperatorNameForPhone(phoneId)
└─> getTelephonyProperty()
You can trace this in the Android “TelephonyManager” source on Googles Android Source Project.
@UnsupportedAppUsage
public static String getTelephonyProperty(int phoneId, String property, String defaultVal) {
String propVal = null;
String prop = SystemProperties.get(property);
if ((prop != null) && (prop.length() > 0)) {
String values[] = prop.split(",");
if ((phoneId >= 0) && (phoneId < values.length) && (values[phoneId] != null)) {
propVal = values[phoneId];
}
}
return propVal == null ? defaultVal : propVal;
}
/**
* Gets a typed per-phone telephony property from a schematized list property.
*/
private static <T> T getTelephonyProperty(int phoneId, List<T> prop, T defaultValue) {
T ret = null;
if (phoneId >= 0 && phoneId < prop.size()) ret = prop.get(phoneId);
return ret != null ? ret : defaultValue;
}
If the Property being read from “build.prop” has a comma “,” like “Verzion,T-Mobile” the Comma is an indication of a Second Value for the Property assuming your Device can and is holding (2) SIM Cards. If the Second SIM is unavailable, it can be reflected as “Verizon,” the second space being empty.
Other functions in that class such as “getDeviceId” will rely off of the Service to get such data.
The “TelephonyManager” class it self utilizes mainly two AIDLs / Interfaces being “com.android.internal.telephony.ITelephony” and “com.android.internal.telephony.IPhoneSubInfo.aidl”, as “TelephonyManager” is just a class less of an reflection of a Interface.
Lets get a list of all active subscriptions now.
//[1] Connect to the Telephony Service
SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
//[2] Call to the Function on the Descriptor
List<SubscriptionInfo> subscriptionInfoList = subscriptionManager.getActiveSubscriptionInfoList();
Once again to us this is about (2) lines of code, first being to connect to the Service responsible for Serving such data, then invoke the function within the AIDL Interface in this case “getActiveSubscriptionInfoList”. The AIDL Interface is “isub” or “android.internal.telephony.ISub” “android/internal/telephony/ISub.aidl”.
Looking at the “AIDL” we see “getActiveSubscriptionInfoList” is the (5th) Function so the Command Call Number is (5). Testing this “service call isub 5” gives us what seems to be Data that is the Active Subscription (SIMs).
How do we know what Interface it is trying to Communicate to ?
We can Hook onto the “android.os.Binder” “transact” function to read the Interface it is trying to Communicate too. Now with our example above sometimes the Interface is only in the “data” parcel but in this case we can just invoke the function “getInterfaceDescriptor” within our Transact Hook.
Doing so will show us the Interfaces that our Application is trying to Communicate too, we have made an application to use the code above to get the Active Subscriptions and well, it logs to the log cat “android.internal.telephony.ISub”.
We will not be diving deep into how the IPC (Inner Process Communication) via Binder works in a deep sense as that is an article for another Time. For now understand “Binder” class on Android is the Class responsible for helping two Processes on Android Communicate to each other (IOCTL) (IPC).
You can view my Small Research I have done on Binders what not here: https://github.com/0bbedCode/DroidBinderTrace
Lets start tracing these events, functions etc, to see what happens on the Other side of these function calls. We as we stated above can do a basic Java Hook onto the “transact” function in the “Binder” but to me that’s lame and known so we will not focus on that but a deeper Hook.
Lets first use a tool called “stackplz”, this will allow us to Hook either Native Functions and or Syscalls. We know the Syscall responsible for communicate two processes is “IOCTL and “/dev/binder” is the Binder Device that will also assist us in this communication.
Lets start with the lowest level possible aka, a “IOCTL” System Call Hook.
We will also not be hooking the Application in question that is doing these transactions as we know it is doing such transactions, we just want to see what happens on the other side. Using our brains we can SAFLEY assume the Service Responsible for handing Telephony Related actions is “com.android.phone”. After all the name of the package is “Phone Services” so it must be.
./stackplz -n com.android.phone -s ioctl --stack
When now invoking our target app that gets some CELL Data we get
[5775|6553|binder:5775_D] ioctl(fd=77, cmd=0xc0306201, arg=0x7201a0da78, ret=0x0)
[5775|6553|binder:5775_D] ioctl(fd=77, cmd=0xc0306201, arg=0x7201a0c068) LR:0x7361b6b578 PC:0x7361bb46ac SP:0x7201a0bf50, Backtrace:
#00 pc 00000000000e86ac /apex/com.android.runtime/lib64/bionic/libc.so (__ioctl+12)
#01 pc 000000000009f574 /apex/com.android.runtime/lib64/bionic/libc.so (ioctl+160)
#02 pc 00000000000663ec /system/lib64/libbinder.so (android::IPCThreadState::talkWithDriver(bool)+284)
#03 pc 00000000000677d4 /system/lib64/libbinder.so (android::IPCThreadState::waitForResponse(android::Parcel*, int*)+64)
#04 pc 0000000000067504 /system/lib64/libbinder.so (android::IPCThreadState::transact(int, unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+224)
#05 pc 000000000007f800 /system/lib64/libbinder.so (android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+236)
#06 pc 00000000001a96bc /system/lib64/libandroid_runtime.so (android_os_BinderProxy_transact(_JNIEnv*, _jobject*, int, _jobject*, _jobject*, int)+156)
#07 pc 00000000001272f0 /data/misc/apexdata/com.android.art/dalvik-cache/arm64/boot.oat (art_jni_trampoline+144)
#08 pc 000000000020a330 /apex/com.android.art/lib64/libart.so (nterp_helper+4016)
#09 pc 00000000005d6890 /system/framework/framework.jar
#10 pc 000000000020b7cc /apex/com.android.art/lib64/libart.so (nterp_helper+9292)
#11 pc 0000000000434e4c /system/framework/framework.jar
#12 pc 000000000020bf04 /apex/com.android.art/lib64/libart.so (nterp_helper+11140)
#13 pc 000000000044be9e /system/framework/framework.jar
#14 pc 000000000020a9d8 /apex/com.android.art/lib64/libart.so (nterp_helper+5720)
#15 pc 000000000044bd74 /system/framework/framework.jar
#16 pc 000000000020a9d8 /apex/com.android.art/lib64/libart.so (nterp_helper+5720)
#17 pc 000000000044bde2 /system/framework/framework.jar
#18 pc 000000000020a9d8 /apex/com.android.art/lib64/libart.so (nterp_helper+5720)
#19 pc 00000000005e07e4 /system/framework/framework.jar
#20 pc 0000000002007308 /memfd:jit-cache (deleted) (offset 0x2000000)
Nothing interesting here, No java Classes, nothing saying “getActiveSubscriptionInfoList”, we can also hook the actual symbols from “libbinder.so”, and we can also see the caller is “binder”.
Understanding “libbinder.so” (/system/lib64/libbinder.so) is going to be the .SO file storing the code for the binder. So lets read all of the Symbols, filter for “transact”
vermeer:/ # readelf -s /system/lib64/libbinder.so | grep transact
596: 00000000000aca48 872 FUNC GLOBAL DEFAULT 16 _ZN7android8RpcState15transactAddressERKNS_2spINS_10RpcSession13RpcConnectionEEEmjRKNS_6ParcelERKNS1_IS2_EEPS7_j
640: 00000000000ad094 344 FUNC GLOBAL DEFAULT 16 _ZN7android8RpcState8transactERKNS_2spINS_10RpcSession13RpcConnectionEEERKNS1_INS_7IBinderEEEjRKNS_6ParcelERKNS1_IS2_EEPSB_j
709: 000000000007c308 1476 FUNC GLOBAL DEFAULT 16 _ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j
1661: 000000000007f714 1376 FUNC GLOBAL DEFAULT 16 _ZN7android8BpBinder8transactEjRKNS_6ParcelEPS1_j
1884: 00000000000a3760 292 FUNC GLOBAL DEFAULT 16 _ZN7android10RpcSession8transactERKNS_2spINS_7IBinderEEEjRKNS_6ParcelEPS6_j
1989: 0000000000067424 584 FUNC GLOBAL DEFAULT 16 _ZN7android14IPCThreadState8transactEijRKNS_6ParcelEPS1_j
Symbols are decorated, this is to prevent functions with the Same names as an ELF Symbols Table will mainly display the name of the Symbol, and if a Class has a function with the Same Name then it will cause confusing. So the symbols then will be decorated with things like “_ZN7” and class info like “android “BBinder” and Function name “transact” with its params.
We will be focusing on "the transact function either from “BBinder” or “BpBinder” as those are used by the “android.os.Binder” class.
./stackplz -n com.android.phone --lib /system/lib64/libbinder.so -w _ZN7android7BBinder8transactEjRKNS_6ParcelEPS1_j --stack
We will now open out target app, and keep trying to invoke the app to invoke the Service, to see if we see output log from “stackplz”. We will right away see a lot but spamming target app with the action can help us identify the logs that are caused from the target app.
Same thing, a bunch of logs, nothing interesting…. This is not really working to our favor, looking back at my Binder research Github I have created a script for this.
This is for “Frida” specifically we will be using “frida-inject” as the Phone Services is a Running Process so it cant re-start it.
./frida-inject -n com.android.phone -s /sdcard/FridaEx/BinderHook.js
Then once again invoking our app to invoke the Service will give us a lot of logs, they are all mixed between “ISub” “IAppOpsService” “ILegacyPermissionManager” and “ITelephony”. We can see from some of the Stack Traces the source of the Code handling the function “getActiveSubscriptionInfoList”
Calling UID=10475
java.lang.Exception
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(Unknown Source:174)
at android.permission.ILegacyPermissionManager$Stub$Proxy.checkPhoneNumberAccess(Unknown Source:36)
at android.permission.LegacyPermissionManager.checkPhoneNumberAccess(Unknown Source:7)
at com.android.internal.telephony.TelephonyPermissions.checkReadPhoneNumber(Unknown Source:23)
at com.android.internal.telephony.TelephonyPermissions.checkCallingOrSelfReadPhoneNumber(Unknown Source:14)
at com.android.internal.telephony.subscription.SubscriptionManagerService.hasPhoneNumberAccess(Unknown Source:2)
at com.android.internal.telephony.subscription.SubscriptionManagerService.conditionallyRemoveIdentifiers(Unknown Source:14)
at com.android.internal.telephony.subscription.SubscriptionManagerService.lambda$getActiveSubscriptionInfoList$16(Unknown Source:6)
at com.android.internal.telephony.subscription.SubscriptionManagerService.$r8$lambda$Q90LYYoiyuY55HEyHtKc2l0la48(Unknown Source:0)
at com.android.internal.telephony.subscription.SubscriptionManagerService$$ExternalSyntheticLambda4.apply(Unknown Source:8)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:205)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:187)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1642)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:503)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:236)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:519)
at com.android.internal.telephony.subscription.SubscriptionManagerService.getActiveSubscriptionInfoList(Unknown Source:104)
at com.android.internal.telephony.ISub$Stub.onTransact(Unknown Source:1095)
at android.os.Binder.execTransactInternal(Unknown Source:109)
at android.os.Binder.execTransact(Unknown Source:39)
Checking the Calling UID (10475) that seems to be the application that we are using to Invoke the Telephony Services. So that checks out and within the Stack Trace we see:
“com.android.internal.telephony.subscription.SubscriptionManagerService.getActiveSubscriptionInfoList”
So this confirms, for one “com.android.phone” is the application handling the Service Descriptors such as “ISub”. Finding the source for “com.android.internal.telephony.subscription.SubscriptionManagerService” will show us this function.
@Override
@Nullable
@RequiresPermission(anyOf = {
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
"carrier privileges",
})
public SubscriptionInfo getActiveSubscriptionInfo(int subId, @NonNull String callingPackage,
@Nullable String callingFeatureId) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext, subId, callingPackage,
callingFeatureId, "getActiveSubscriptionInfo")) {
throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or "
+ "carrier privilege");
}
SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager
.getSubscriptionInfoInternal(subId);
if (subInfo != null && subInfo.isActive()) {
return conditionallyRemoveIdentifiers(subInfo.toSubscriptionInfo(), callingPackage,
callingFeatureId, "getActiveSubscriptionInfo");
}
return null;
}
We see “mSubscriptionDatabaseManager” , so it seems to be now contacting Some Database ? Lets get a better look by transferring the APK “com.android.phone” to our work environment to reverse engineer it.
Nothing here that is from the class “com.android.internal.telephony.subscription.SubscriptionManagerService”, perhaps it is loading it in dynamically or the zygote is loading in the code ? It can also be from the “framework.jar” on your devices located in “/system/framework/”
Lets try this
vermeer:/ # grep -ril "SubscriptionController" /system/
/system/etc/preloaded-classes
/system/framework/telephony-common.jar
grep: /system/media: Is a directory
grep: /system/product: Is a directory
grep: /system/system_ext: Is a directory
grep: /system/usr/icu: Is a directory
grep: /system/vendor: Is a directory
To my eyes what spikes my interest is “telephony-common.jar” so I move it over to my environment again to Decompile it. We find our target code
Going into the function “getSubscriptionInfoInternal” we see it is trying to read it from some cache, so it will initialize cache from some where…
Looking around in this class we find a function called “loadDatabaseInternal” and it seems to be Communicating to another Service / Process “.query(Telephony.SimInfo.CONTENT_URI….”
The URI for “Telephony.SimInfo” seems to be “com.android.providers.telephony” , this makes sense as android between the services will at times have a Provider Package. A package that manages Files / Data / Databases etc for Services and or Requesting Applications. In this case it is “Phone and Messaging Storage”
Looking at the Application Directory “/data/user_de/0/com.android.providers.telephony/” we see a folder called “databases” going into it gives us:
1|vermeer:/ # ls /data/user_de/0/com.android.providers.telephony/databases
CarrierInformation.db HbpcdLookup.db carrierIdentification.db mmssms.db telephony.db
CarrierInformation.db-journal HbpcdLookup.db-journal carrierIdentification.db-journal mmssms.db-journal telephony.db-journal
Moving these files to our work environment to inspect, we first look at the “telephony.db” Database as that seems most interesting. We use SQliteStudio we see
As you see, this seems to be all the SIM Cards I have ever inserted into my device. First one being the active one told by the fact that the SIM ID Column is (0) aka in Slot (0) aka Slot (1) while the others are in (-1). This is from the “siminfo” table, looking at the “carriers” table we see
Carrier information, like the MCC and MNC values, their IDs, ports, and a lot more. This is a list of most of the carriers as I do not have most of these carriers or ever used them but its more a resolution table to get info when needed for a specific carrier.
[APP] => getActiveSubscriptionInfo()
TRANSACT(COMMAND, DATA, REPLY, FLAGS)
=> [com.android.phone]
=> Find Interface Caller is trying to Communicate too "com.android.internal.telephony.ISub"
=> QUERY(Telephony.SimInfo)
=> [com.android.providers.telephony]
=> /databases/telephony.db -> [siminfo]
We will get into more how the other APIs work communicate other Interfaces more in depth look including getting files from the physical sim card it self but for now this is all. I am burnt out when it comes to writing articles right now but this will be continued soon.
Subscribe to my newsletter
Read articles from MD directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
