I'm implementing a Service (but currently i'm testing the code in an Activity) that detects SIM swap/insertion/removal. When SIM change is detected, i plan to announce the event to other parts of app and store new MSISDN for later use (or clear the stored one if no sim is present).
I used to listen to android.intent.action.SIM_STATE_CHANGED broadcast, but as it's not supported by public SDK API, I've decided to switch to TelehoneManager and PhoneStateListener, listening to service state changes (as described here and here).
Unfortuntely, it seems to be unreliable. Sometimes, after swapping the SIM to a new one, and pulling it out and then inserting back again, IN_SERVICE event is not fired (the phone shows mobile network connection being active, Android Settings show service state as 'In service', but listener does not receive the event).
Also, I've noticed that after swapping to other SIM (without device restart), almost every time when I pull it out and insert back (to fire the events) and listener receives the IN_SERVICE event, SIM state (obtained by TelephonyManager.getSimState()) is not yet SIM_STATE_READY, and I can't obtain the MSISDN (using TelephonyManager.getLine1Number()). A short while after this, I could see MSISDN number in Android Settings, so it's readable, just not yet available when the event comes.
When removing and inserting back the same SIM that was in the phone when the app run for the first time, MSISDN is almost always available when IN_SERVICE event comes.
I've tested this on a few devices (all are Android 5.1) and SIM cards, the behavior is same so it's rather not faulty SIM nor device.
Of course, i have included READ_PHONE_STATE permission in a manifest.
example code:
public static void testListener(Context context) {
TelephonyManager telephonyMgr = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
PhoneStateListener listener = new PhoneStateListener(){
@Override
public void onServiceStateChanged(ServiceState serviceState) {
super.onServiceStateChanged(serviceState);
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
//StorageUtil is a simple helper class to store/load values from SharedPreferences
StorageUtil storageUtil = new StorageUtil(context);
String oldSim = storageUtil.getStringValue(LAST_SIM_STATE_KEY);
String newSim = getMsisdn(context);
String oldImsi = storageUtil.getStringValue(LAST_SIM_IMSI_KEY);
String newImsi = getImsi(context);
Log.d("SIM", MessageFormat.format("{0} === {1} -> {2} [{3} -> {4}] {5}", decodeServiceState(serviceState.getState()), oldSim, newSim, oldImsi, newImsi, tm.getSimState()));
storageUtil.storeValue(LAST_SIM_STATE_KEY, newSim);
storageUtil.storeValue(LAST_SIM_IMSI_KEY, newImsi);
}
};
telephonyMgr.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
}
public static String getImsi(Context context) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return tm.getSubscriberId();
}
public static String getMsisdn(Context context) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return tm.getLine1Number();
}
static private String decodeServiceState(int state) {
switch(state) {
case 0:
return "IN_SERVICE ";
case 1:
return "OUT_OF_SERVICE";
case 2:
return "EMERGENCY_ONLY";
case 3:
return "POWER_OFF ";
}
return "";
}
example logs (with comments) showing problem occurence: http://pastebin.com/wUdGH0Hu
Is there a way to fix those issues ? They seem to be pretty random and not easy to reproduce, but they keep happening and ruining the day. Or maybe I shouldn't use the PhoneStateListener at all ?