title: Android - Navigating through USSD menu using pre-determined inputs sent via requests
body: <p>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.</p>
  
  <p>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.</p>
  
  <p>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.</p>
  
  <p>I am currently exploring that option and this is what I've tried so far. <a href="https://stackoverflow.com/a/50977125/1374416">Heavily lifted from this StackOverflow Question.</a></p>
  
  <pre><code>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();
          }
      }
  }
  </code></pre>
  
  <p>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 <code>notifyUssdResult</code> 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. </p>
  
  <p>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. </p>
  
  <p>Is there any samples I can follow?</p>