Android - Navigating through USSD menu using pre-determined inputs sent via requests

妖精的绣舞 提交于 2019-12-08 01:54:39

问题


I am planning to create an Android App that will automate sending a users response to a USSD Menu. At a click of a button, the app will send the initial code, followed by the rest of the menu inputs.

For example, the initial number is *143#, followed by 1, 1, 1, and a user PIN. I'd like to be able to automate that sequence of inputs so that the user won't have to input it on their own.

I know that in Android Oreo, they implemented a USSD Callback using the TelephonyManager, where the Android App can send a USSD Request, and then read the response given.

I am currently exploring that option and this is what I've tried so far. Heavily lifted from this StackOverflow Question.

interface UssdResultNotifiable {
    void notifyUssdResult(String request, String returnMessage, int resultCode);
}


public class MainActivity extends Activity implements UssdResultNotifiable {

    USSDSessionHandler hdl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void onUssdSend(View view) {
        hdl = new USSDSessionHandler(MainActivity.this, MainActivity.this);
        hdl.doSession(((EditText) this.findViewById(R.id.ussdText)).getText().toString());
    }

    @Override
    public void notifyUssdResult(final String request, final String returnMessage, final int resultCode) {
        this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MainActivity.this, "Request was " + request + "\n response is "
                        + returnMessage + "\n result code is " + resultCode, Toast.LENGTH_LONG).show();

                Log.e(TAG, "ussd result hit! Response is = " + returnMessage);

                hdl.doSession("1");
            }
        });

    }

}

class USSDSessionHandler {

    TelephonyManager tm;
    private UssdResultNotifiable client;
    private Method handleUssdRequest;
    private Object iTelephony;

    USSDSessionHandler(Context parent, UssdResultNotifiable client) {
        this.client = client;
        this.tm = (TelephonyManager) parent.getSystemService(Context.TELEPHONY_SERVICE);
        try {
            this.getUssdRequestMethod();
        } catch (Exception ex) {
            //log
        }

    }

    private void getUssdRequestMethod() throws ClassNotFoundException, NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {
        if (tm != null) {
            Class telephonyManagerClass = Class.forName(tm.getClass().getName());
            if (telephonyManagerClass != null) {
                Method getITelephony = telephonyManagerClass.getDeclaredMethod("getITelephony");
                getITelephony.setAccessible(true);
                this.iTelephony = getITelephony.invoke(tm); // Get the internal ITelephony object
                Method[] methodList = iTelephony.getClass().getMethods();
                this.handleUssdRequest = null;

                for (Method _m : methodList)
                    if (_m.getName().equals("handleUssdRequest")) {
                        handleUssdRequest = _m;
                        break;
                    }
            }
        }
    }

    @android.support.annotation.RequiresApi(api = Build.VERSION_CODES.N)
    public void doSession(String ussdRequest) {
        try {

            if (handleUssdRequest != null) {
                handleUssdRequest.setAccessible(true);
                handleUssdRequest.invoke(iTelephony, SubscriptionManager.getDefaultSubscriptionId(), ussdRequest, new ResultReceiver(new Handler()) {

                    @Override
                    protected void onReceiveResult(int resultCode, Bundle ussdResponse) {
                        Object p = ussdResponse.getParcelable("USSD_RESPONSE");

                        if (p != null) {

                            Method[] methodList = p.getClass().getMethods();
                            for(Method m : methodList){
                                Log.e(TAG, "onReceiveResult: " + m.getName());
                            }
                            try {
                                CharSequence returnMessage = (CharSequence) p.getClass().getMethod("getReturnMessage").invoke(p);
                                CharSequence request = (CharSequence) p.getClass().getMethod("getUssdRequest").invoke(p);
                                USSDSessionHandler.this.client.notifyUssdResult("" + request, "" + returnMessage, resultCode); //they could be null
                            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                });
            }
        } catch (IllegalAccessException | InvocationTargetException e1) {
            e1.printStackTrace();
        }
    }
}

What you get is an app that would ask for a USSD Input for the user, and once the "Send" button is hit, the USSD Response is displayed via a Toast on the notifyUssdResult function. In that same function, I send the next input in my sequence which is "1". Am I able to once again send a reply to the USSD, and the USSD takes it as the input and goes to the next menu.

However, as soon as I send the reply, the USSD menu shows up in my device and I'm unable to proceed further. I am unable to navigate the USSD Menu purely through my app, as the USSD Menu interferes with the screen and doesn't go away.

Is there any samples I can follow?

来源:https://stackoverflow.com/questions/55330850/android-navigating-through-ussd-menu-using-pre-determined-inputs-sent-via-requ

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!