interactive ussd session(multi step) does not work on android 8(Oreo)

I am currently working with Telephony Manager(USSD response) available in android api level 26(Nexus 6P). For single step ussd session, it's working.

reference: http://codedrago.com/q/140674/android-telephony-telephonymanager-ussd-android-8-0-oreo-does-android-8-0-api-26-support-sending-and-repying-to-ussd-messages

example:

USSD request : "A" (ussd session initiates)

USSD response : "X" (ussd session terminates)

    TelephonyManager =  telephonyManager(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
    Handler handler = new Handler();
    TelephonyManager.UssdResponseCallback callback = new TelephonyManager.UssdResponseCallback() {
        @Override
        public void onReceiveUssdResponse(TelephonyManager telephonyManager, String request, CharSequence response) {
            super.onReceiveUssdResponse(telephonyManager, request, response);
            Log.e("ussd",response.toString());

        }

        @Override
        public void onReceiveUssdResponseFailed(TelephonyManager telephonyManager, String request, int failureCode) {
            super.onReceiveUssdResponseFailed(telephonyManager, request, failureCode);
            Log.e("ussd","failed with code " + Integer.toString(failureCode));
        }
    };

    try {
           Log.e("ussd","trying to send ussd request");
           telephonyManager.sendUssdRequest("*123#",
                    callback,
                    handler);
        }catch (Exception e){


            String msg= e.getMessage();
            Log.e("DEBUG",e.toString());
            e.printStackTrace();
        }

but for interactive ussd request-response(multi-step), it's not working. Multi step scenario is as follows:

step # 1.

USSD request : "A" (ussd session initiates)

USSD response : "X"

step # 2.

USSD request : "B" (ussd session continues)

USSD response : "Y"

step # 3.

USSD request : "C"

USSD response : "Z" (ussd session terminates)

I am able to get a menu, respond to it by sending a number but past that, I guess am at the mercy of the telco - they send a menu that blocks the entire screen and if you cancel it the entire session dies. Having worked in telecom, I think that this might be different from telco to telco because some of them have gateways that can kill a user initiated session and replace it with a telecom side originated session. technically, the 2 sessions are detached. but here is my code:

    package org.rootio.test.telephony.telephonyautorespond;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
import android.telecom.TelecomManager;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.Switch;
import android.widget.Toast;

import com.google.android.material.snackbar.Snackbar;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import androidx.annotation.RequiresApi;

import static android.content.ContentValues.TAG;

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

public class HomeActivity extends Activity implements UssdResultNotifiable {

    USSDSessionHandler hdl;
    private TelephonyManager telephonyManager;
    private PhoneCallListener listener;
    private TelecomManager telecomManager;

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

    public void onUssdSend(View view) {
        //USSDHandler callback = new USSDHandler(view);
        /* if (checkSelfPermission(Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
         *//*if(ActivityCompat.shouldShowRequestPermissionRationale(HomeActivity.this, Manifest.permission.CALL_PHONE))
                    {

                    }
                    else
                    {*//*
                        //ActivityCompat.requestPermissions(HomeActivity.this, new String[]{Manifest.permission.CALL_PHONE}, 0);
                   // }
                    Snackbar.make(view, "permissions were missing", Snackbar.LENGTH_LONG)
                            .setAction("Response", null).show();
                    return;
                }*/
        //HomeActivity.this.telephonyManager.sendUssdRequest("*#123*99#", callback, new Handler());



        hdl = new USSDSessionHandler(HomeActivity.this, HomeActivity.this);

        hdl.doSession(((EditText)this.findViewById(R.id.ussdText)).getText().toString());

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_home, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    public void toggleListener(View v) {
        if (((Switch) v).isChecked()) {
            this.listenForTelephony();
            Toast.makeText(this, "Listening for calls", Toast.LENGTH_LONG).show();
        } else {
            this.stopListeningForTelephony();
        }
    }

    private void listenForTelephony() {
        this.telephonyManager = (TelephonyManager) this.getSystemService(this.TELEPHONY_SERVICE);
        this.telecomManager = (TelecomManager) this.getSystemService(this.TELECOM_SERVICE);
        this.listener = new PhoneCallListener();
        telephonyManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
    }

    private void stopListeningForTelephony() {
        this.telephonyManager = null;
        this.telecomManager = null;
    }

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

            }
        });

    }


    class PhoneCallListener extends PhoneStateListener {
        @RequiresApi(api = Build.VERSION_CODES.M)
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {

            switch (state) {
                case TelephonyManager.CALL_STATE_RINGING:
                    HomeActivity.this.telecomManager.acceptRingingCall();
                    break;
                case TelephonyManager.CALL_STATE_IDLE:
                    Toast.makeText(HomeActivity.this, "Call is no longer active...", Toast.LENGTH_LONG);
                    break;
            }
        }
    }

    @TargetApi(Build.VERSION_CODES.O)
    class USSDHandler extends TelephonyManager.UssdResponseCallback {

        View parent;

        USSDHandler(View v) {
            this.parent = v;
        }

        @Override
        public void onReceiveUssdResponse(TelephonyManager telephonyManager, String request, CharSequence response) {
            super.onReceiveUssdResponse(telephonyManager, request, response);
            Snackbar.make(this.parent, response, Snackbar.LENGTH_LONG)
                    .setAction("Response", null).show();
        }

        @Override
        public void onReceiveUssdResponseFailed(TelephonyManager telephonyManager, String request, int failureCode) {
            super.onReceiveUssdResponseFailed(telephonyManager, request, failureCode);
            Snackbar.make(this.parent, "error is " + failureCode + " for req " + request, Snackbar.LENGTH_LONG)
                    .setAction("Response", null).show();
        }
    }


}

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;
                /*
                 *  Somehow, the method wouldn't come up if I simply used:
                 *  iTelephony.getClass().getMethod('handleUssdRequest')
                 */

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

    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) {
                        /*
                         * Usually you should the getParcelable() response to some Parcel
                         * child class but that's not possible here, since the "UssdResponse"
                         * class isn't in the SDK so we need to
                         * reflect again to get the result of getReturnMessage() and
                         * finally return that!
                         */

                        Object p = ussdResponse.getParcelable("USSD_RESPONSE");

                        if (p != null) {

                            Method[] methodList = p.getClass().getMethods();
                            for(Method m : methodList)
                            {
                                Log.i(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();
        }
    }
}

please ignore the call answering stuff - I was using this app before to test automatic call answering on Oreo. And below is the Layout file for the display:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".HomeActivity">


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="USSD Input"
            android:textSize="18sp" />

        <EditText
            android:id="@+id/ussdText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="10"
            android:inputType="textPersonName" />
    </LinearLayout>

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onUssdSend"
        android:text="send" />
</LinearLayout>

Sending USSD request in one session, wwwserver2 Jul 8 '19 at 7:56 Unfortunately, this is not a client matter but a server matter. that Google added in Oreo only works for USSD services where you can Android SDK so that you can get this functionality in your own app. configurations for USSD services then trigger the session to run from  I seen many guides how to accomplish this (Documentation, Tutorial) and successfully made it happen in android 7. What I managed to do is successfully get notification with service running at background when user activity changed. My app works perfectly. The problem is with android 8 and google battery management. My service is always killed. So

I managed to bypass some of the problems (like the multisession USSD responses not working) by using Reflection. I've made a GitHub gist here.

Obviously the assumption is that, the correct permissions (only CALL_PHONE at this point) are given - In terms of Contexts, I've only ever run this in an Activity but I think it would work fine in most if not all/any.

The next thing is figuring out how to sustain the session, if at all possible.

interactive ussd session(multi step) does not , interactive ussd session(multi step) does not work on android 8(Oreo). I am currently working with Telephony Manager(USSD response)  Unfortunately the API that Google added in Oreo only works for USSD services where you can dial the entire USSD code at the start and get back the response without entering anything into the session. What they apparently don't realize is that most telcos prevent this for security reasons, especially when there is a PIN entry.

These APIs don't properly handle menu-based USSDs. Their intended use case would be for things like querying simple things like minutes or data left in the user's plan. For menu-based systems there would need to be some notion of a continued session, which is not something the APIs support.

St4k, interactive ussd session(multi step) does not work on android 8(Oreo). android​ussdandroid-8.0-oreo. Oct 1 '17 22:26. Zoraf. 171. 0. votes. 0. answers. 24. views. Introduction - This question has been updated the 27th May 2018: I have 1 PHP multidimensional-array, containing 6 sub-arrays, each containing 20 sub-sub-arrays, which in turn, each contain 2 sub-sub-arrays, one being a string (header), the other being an unspecified number of keywords (keywords).

public class MainActivity extends AppCompatActivity {
private Button dail;
private String number;
private TelephonyManager telephonyManager;

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


    telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

    dail = (Button) findViewById(R.id.dail);
    dail.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
                    runtimepermissions();
                    return;
                }else{
                    telephonyManager.sendUssdRequest("*121#", new TelephonyManager.UssdResponseCallback() {
                        @Override
                        public void onReceiveUssdResponse(TelephonyManager telephonyManager, String request, CharSequence response) {
                            super.onReceiveUssdResponse(telephonyManager, request, response);

                            Log.d("Received response","okay");
                            ((TextView)findViewById(R.id.response)).setText(response);
                        }

                        @Override
                        public void onReceiveUssdResponseFailed(TelephonyManager telephonyManager, String request, int failureCode) {
                            super.onReceiveUssdResponseFailed(telephonyManager, request, failureCode);
                            Log.e("ERROR ","can't receive response"+failureCode);
                        }
                    },new Handler(Looper.getMainLooper()){
                        @Override
                        public void handleMessage(Message msg) {
                            Log.e("ERROR","error");
                        }
                    });
                }

        }
    });

    }
    public boolean runtimepermissions() {
        if (Build.VERSION.SDK_INT >= 23 && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED
                && ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE, Manifest.permission.CALL_PHONE}, 100);
            return true;
        }
        return false;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 100) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
                Log.d("PERMISSIONS","granted");
               // doJob();
            } else {
                runtimepermissions();
            }
        }

}

}

if your using dual sim make sure change to the network that your going to test(In my i used Airtel(*121#).

Ussd Android Api Level, interactive ussd session(multi step) does not work on android 8(Oreo). I am currently working with Telephony Manager(USSD response) available in android api  Working with Change Data Capture in InfluxDB-Pytho When click on second dropdown black box should rem Android Oreo killing background services and clear Why does LTO introduce new DT flags TLSDESC_PLT an Voice over is not reading what I am typing in text Generating video or audio using raw PCM

Android, Solved: API Level 8 for Android - SmartBear Community. Android Oreo Allows Apps to Read USSD Messages from Carriers; Hemant Vishwakarma: interactive ussd Android Forums; interactive ussd session(multi step) does not work on . I've got a complex object in a JSON format. I'm using Knockout Mapping, customizing the create callback, and trying to make sure that every object that should be an observable - would actually be mapped as such.

Ussd api download and save, interactive ussd session(multi step) does not work on android I know that in Android Oreo, they implemented a USSD Callback using the  Working with Change Data Capture in InfluxDB-Pytho When click on second dropdown black box should rem Android Oreo killing background services and clear Why does LTO introduce new DT flags TLSDESC_PLT an Voice over is not reading what I am typing in text Generating video or audio using raw PCM

Android oreo ussd, Restcomm the only open source full stack communications. Ussd can encompass once off requests or interactive navigation with a menu If there is no response then the gsm modem probably does not support ussd The hover api supports multistep, multisim dialing and navigating of ussd sessions on android version. Autofill causes apps to crash on Android 8.0, 8.1 In Android 8.0 (API level 26) and 8.1 (API level 27), autofill can cause your app to crash in certain scenarios. To work around any potential issues, you should tag any views that are not autofilled with importantForAutofill=no .

Comments
  • Hello. I'm having the same issue right now. How did you solve yours ? Added to that I only get error code -1 ...
  • Till now I don't find any solution
  • Glad you answered I forgot about my comment on this post... Actually I did not even read your post properly...Since I was trying multi-step USSD, it failed. I resorted to stick to single-step USSD. Now for multistep, maybe you'll need to override something in sendUssdRequest. I found that there is a method there of something like onReceiveResult that catches the message for multi step but it does not know how to continue processing the request... Just forget about multi-step for now or o integrate it in you UI...
  • I have found an alternative solution by using android accessibility service for receiving USSD response and give input
  • @zoraf can u share the the link of that alternate solution and is there any solution of backward compatibility below than 26 of UssdResponseCallback
  • any proposition on this one? I cannot get the handleUssdRequest method!
  • Good job, we need more guys like you around here !
  • in your sample, what does the l refer to in new ResultReceiver(l){?
  • @Razgriz It refers to some Handler type object I had in context, typically the main UI thread handler. So to instantiate one, you could do something like this: Handler l = new Handler(Looper.getMainLooper()); If you're executing in the main UI thread already, then you can omit the reference to the main looper and just have new Handler();.
  • That's sad. Sometimes the carriers doesn't offer a direct way to access your internet plan or something else, only with a continued menu.