/******************************************************************************* * Copyright 2015, 2016 alladin-IT GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ package at.alladin.rmbt.android.util; import java.lang.reflect.Method; import java.util.concurrent.atomic.AtomicBoolean; import android.content.Context; import android.os.Build; import android.telephony.TelephonyManager; import android.util.Log; /** * @author leo * */ public abstract class DualSimDetector { private static final String TAG = "DualSimDetector"; static { DualSimNewApiWrapper wrapper = null; try { if (DualSimNewApiWrapper.checkAvailable()) wrapper = new DualSimNewApiWrapper(); } catch (Throwable e) { //e.printStackTrace(); } DUAL_SIM_NEW_API_WRAPPER = wrapper; } private final static DualSimNewApiWrapper DUAL_SIM_NEW_API_WRAPPER; private final static String DUAL_SIM_METHOD_API = "api_" + Build.VERSION.SDK_INT; public static class DualSimDetectedException extends Exception { private static final long serialVersionUID = 1L; private final String method; private final String detail; public DualSimDetectedException(String method, String detail) { this.method = method; this.detail = detail; } public DualSimDetectedException(String method) { this(method, ""); } public String getMethod() { return method; } public String getDetail() { return detail; } } public static String getDualSIM(Context ctx) { try { checkDualSIM(ctx); return null; } catch (DualSimDetectedException e) { return e.getMethod(); } } public static void checkDualSIM(Context ctx) throws DualSimDetectedException { try { if (DUAL_SIM_NEW_API_WRAPPER != null) { final boolean dualSim = DUAL_SIM_NEW_API_WRAPPER.isDualSim(ctx); if (dualSim) throw new DualSimDetectedException(DUAL_SIM_METHOD_API); else return; // other methods do not need to be checked in this case (hopefully...) } // printTelephonyManagerMethodNamesForThisDevice(ctx); tryVoodooMethods(ctx); } catch (DualSimDetectedException e) { Log.i(TAG, "dual sim detected: " + e.getMethod() + "; " + e.getDetail()); throw e; // pass dual sim exc. } catch (Throwable t) { // do not fail! } } /** * if android dual sim api is not available, try to find out with undocumented methods if we * are dealing with a dual sim device * @return */ private static void tryVoodooMethods(Context ctx) throws DualSimDetectedException { final TelephonyManager tm = ((TelephonyManager) ctx .getSystemService(Context.TELEPHONY_SERVICE)); tryMethod("tm.gnog(i/1)", tm, "getNetworkOperatorGemini", int.class, 1); tryMethod("tm.gno(i/1)", tm, "getNetworkOperator", int.class, 1); // NO, NO! Do NOT do this: ////////xxtryMethod(tm, "getNetworkOperator", long.class, 1); // this leads to problems with s4 (see #1245) tryMethod("tm.gsig(i/1)", tm, "getSubscriberIdGemini", int.class, 1); tryMethod("tm.gsi(i/1)", tm, "getSubscriberId", int.class, 1); // this seems to be problematic on titan_umtsds / Motorola G 2014 according to dz tryMethod("tm.gsi(l/1)", tm, "getSubscriberId", long.class, 1); } private static void tryMethod(String detectionMethod, TelephonyManager telephonyManager, String predictedMethodName, Class<?> paramClass, int slotID) throws DualSimDetectedException { try { final Class<?> telephonyClass = Class.forName(telephonyManager.getClass().getName()); final Class<?>[] parameter = new Class[1]; parameter[0] = paramClass; final Method method = telephonyClass.getMethod(predictedMethodName, parameter); final Object[] param = new Object[1]; param[0] = slotID; final Object result = method.invoke(telephonyManager, param); final String resultString = method + " (" + slotID + ") = " + result; Log.d(TAG, resultString); if (result != null && !result.equals("")) throw new DualSimDetectedException(detectionMethod, resultString); } catch (DualSimDetectedException e) { throw e; // pass dual sim exc. } catch (Throwable e) { // ignore failures } } private static AtomicBoolean METHODS_PRINTED = new AtomicBoolean(); // for debugging public static void printTelephonyManagerMethodNamesForThisDevice(Context context) { if (!METHODS_PRINTED.compareAndSet(false, true)) return; // only once try { final TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); final Class<?> telephonyClass = Class.forName(telephony.getClass().getName()); final Method[] methods = telephonyClass.getMethods(); for (int idx = 0; idx < methods.length; idx++) System.out.println("\nMethod: " + methods[idx] + " declared by " + methods[idx].getDeclaringClass()); } catch (Throwable e) { // do not fail! } } }