21

i did many research about ussd but I can't read USSD respone and how can I prevent ussd dialog like this application.

https://play.google.com/store/apps/details?id=com.iba.ussdchecker&hl=en

i install this https://github.com/alaasalman/ussdinterceptor restart my phone (S2 android 4.0.3), send ussd code but nothing happen, someone tell me that I have to read the log, but how can i do that.

i try these code to read USSD from log

USSD.java

    package com.example.ussd;

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.Calendar;
    import java.util.Date;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;

    import android.util.Log;

    public class USSD {

        private static String startmsg = "displayMMIComplete"; // start msg to look
                                                                // for
        private static String endmsg = "MMI code has finished running"; // stop msg
        private static String trimmsg = "- using text from MMI message: '"; // a msg
                                                                            // to
                                                                            // remove
                                                                            // from
                                                                            // the
                                                                            // text

        private long before = 3000; // delay (ms) before creation of the class
                                    // before a msg (USDD) is valid (use timestamp)
        private long after = 3000; // delay (ms) after creation of the class that a
                                    // msg (USDD) is valid (wait after ms)

        private String msg = ""; // the USSD message
        private boolean found = false;
        private long t = -1; // timestamp of the found log

        public USSD() {
            this(3000, 3000);
        }

        // USSD in log : example
        public USSD(long before_creation, long after_creation) {
            before = before_creation;
            after = after_creation;
            long timestamp = System.currentTimeMillis(); // creation of the class
                                                            // --> look for the USSD
                                                            // msg in the logs
            Log.d("USSDClass",
                    "Class creation - timestamp: " + String.valueOf(timestamp));
            try {
                // sample code taken from alogcat ...
                Process logcatProc = Runtime.getRuntime().exec(
                        "logcat -v time -b main PhoneUtils:D"); // get PhoneUtils
                                                                // debug log with
                                                                // time information
                BufferedReader mReader = new BufferedReader(new InputStreamReader(
                        logcatProc.getInputStream()), 1024);
                String line = "";
                boolean tostop = false;
                long stop = timestamp + after; // to stop the while after "after" ms
                while (((line = mReader.readLine()) != null)
                        && (System.currentTimeMillis() < stop) && (tostop == false)) {
                    if (line.length() > 19) // the line should be at least with a
                                            // length of a timestamp (19) !
                    {
                        if (line.contains(startmsg)) // check if it is a USSD msg
                        {
                            // log example :
                            // "12-10 20:36:39.321 D/PhoneUtils(  178): displayMMIComplete: state=COMPLETE"
                            t = extracttimestamp(line); // extract the timestamp of
                                                        // thie msg
                            Log.d("USSDClass", "Found line at timestamp : "
                                    + String.valueOf(t));
                            if (t >= timestamp - before)
                                found = true; // start of an USDD is found & is
                                                // recent !
                        } else if (found) {
                            // log example :
                            // "12-10 20:36:39.321 D/PhoneUtils(  178): displayMMIComplete: state=COMPLETE"
                            if (line.contains(endmsg))
                                tostop = true;
                            else {
                                // log example :
                                // "12-10 20:36:39.321 D/PhoneUtils(  178): - using text from MMI message: 'Your USSD message with one or several lines"
                                Log.d("USSDClass", "Line content : " + line);
                                String[] v = line.split("\\): "); // doesn't need
                                                                    // log
                                                                    // information
                                                                    // --> split
                                                                    // with "): "
                                                                    // separator
                                if (v.length > 1)
                                    msg += v[1].replace(trimmsg, "").trim() + "\n";

                            }
                        }
                    }
                }
            } catch (IOException e) {
                Log.d("USSDClass", "Exception:" + e.toString());
            }
        }

        public boolean IsFound() {
            return found;
        }

        public String getMsg() {
            return msg;
        }

        // extract timestamp from a log line with format
        // "MM-dd HH:mm:ss.ms Level/App:msg" Example : 12-10 20:36:39.321
        // Note : known bug : happy new year check will not work !!!
        private long extracttimestamp(String line) {
            long timestamp = -1; // default value if no timestamp is found
            String[] v = line.split(" ");
            if (v.length > 1) // check if there is space
            {
                Calendar C = Calendar.getInstance();
                int y = C.get(Calendar.YEAR);
                String txt = v[0] + "-" + y + " " + v[1]; // transform in format
                                                            // "MM-dd-yyyy HH:mm:ss"
                SimpleDateFormat formatter = new SimpleDateFormat(
                        "MM-dd-yyyy HH:mm:ss");
                try {
                    Date tmp = formatter.parse(txt);
                    timestamp = tmp.getTime();
                    String[] ms = v[1].split("."); // get ms
                    if (ms.length > 1)
                        timestamp += Integer.getInteger(ms[1]);

                } catch (ParseException e) {
                    Log.d("USSDClass",
                            "USDD.extractimestamp exception:" + e.toString());
                }
            }
            return timestamp;

        }

    }

UssdActivity.java

package com.example.ussd;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.TextView;

public class UssdActivity extends Activity implements OnClickListener {
    /** Called when the activity is first created. */
    private TextView view;
    private AutoCompleteTextView number;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button button = (Button) findViewById(R.id.button1);
        button.setOnClickListener(this);
        this.view = (TextView) findViewById(R.id.Text2);
        this.number = (AutoCompleteTextView) findViewById(R.id.Text1);
    }

    @Override
    public void onClick(View arg0) {
        String encodedHash = Uri.encode("#");
        call("*" + number.getText() + encodedHash);
        this.view.setText("");
    }

    protected void call(String phoneNumber) {
        try {
            startActivityForResult(
                    new Intent("android.intent.action.CALL", Uri.parse("tel:"
                            + phoneNumber)), 1);
        } catch (Exception eExcept) {
            this.view.append("\n\n " + "\n" + eExcept.toString());
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        USSD ussd = new USSD(4000, 4000);
        if (ussd.IsFound())
            this.view.append("\n test" + ussd.getMsg());
        else
            this.view.append("" + R.string.error_ussd_msg);
    }

}

and output is something like this:

com.sec.android.app.callsetting.allcalls:com.sec.android.callsetting.allcalls.AllCallsProvider Terminated ALSA PLAYBACK device hifi
Jack
  • 724
  • 1
  • 8
  • 15

3 Answers3

42

It is possible using accessibility service. First create a service class:

public class USSDService extends AccessibilityService {

    public static String TAG = USSDService.class.getSimpleName();

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        Log.d(TAG, "onAccessibilityEvent");

        AccessibilityNodeInfo source = event.getSource();
        /* if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && !event.getClassName().equals("android.app.AlertDialog")) { // android.app.AlertDialog is the standard but not for all phones  */
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && !String.valueOf(event.getClassName()).contains("AlertDialog")) {
            return;
        }
        if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && (source == null || !source.getClassName().equals("android.widget.TextView"))) {
            return;
        }
        if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && TextUtils.isEmpty(source.getText())) {
            return;
        }

        List<CharSequence> eventText;

        if(event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            eventText = event.getText();
        } else {
            eventText = Collections.singletonList(source.getText());
        }

        String text = processUSSDText(eventText);

        if( TextUtils.isEmpty(text) ) return;

        // Close dialog
        performGlobalAction(GLOBAL_ACTION_BACK); // This works on 4.1+ only

        Log.d(TAG, text);
        // Handle USSD response here

    }

    private String processUSSDText(List<CharSequence> eventText) {
        for (CharSequence s : eventText) {
            String text = String.valueOf(s);
            // Return text if text is the expected ussd response
            if( true ) {
                return text;
            }
        }
        return null;
    }

    @Override
    public void onInterrupt() {
    }

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        Log.d(TAG, "onServiceConnected");
        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
        info.flags = AccessibilityServiceInfo.DEFAULT;
        info.packageNames = new String[]{"com.android.phone"};
        info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
        setServiceInfo(info);
    }
}

Declare it in Android manifest

<service android:name=".USSDService"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService" />
        </intent-filter>
        <meta-data android:name="android.accessibilityservice"
            android:resource="@xml/ussd_service" />
 </service>

Create a xml file that describes the accessibility service called ussd_service

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagDefault"
android:canRetrieveWindowContent="true"
android:description="@string/accessibility_service_description"
android:notificationTimeout="0"
android:packageNames="com.android.phone" />

That's it. Once app is installed, you have to enable the service in Accessibility Settings (Setting->Accessibility Setting -> YourAppName).

Solution described here and here (russian).

Nayan
  • 1,521
  • 2
  • 13
  • 27
HenBoy331
  • 1,915
  • 16
  • 20
  • This works perfect on lollipop, but no success on 4.2.2 and 4.0.3.? – Umesh Isran Dec 18 '15 at 11:54
  • I gues this problem is device specific. The above code works for Samsung Duos(4.0.4), One plus one(5.1.1), Xiomi mi(4.4), but fails to work on Micromax A177(4.2.2), Micromax A905(4.0.4), Lenovo (4.2.2). Please if you can check the same and look for a solution.. – Umesh Isran Dec 21 '15 at 07:43
  • 2
    Yes on some devices it does not work. On some devices it is com.android.phone.UssdAlertActivity instead of android.app.AlertDialog – HenBoy331 Dec 21 '15 at 10:08
  • 1
    Please check my new answer. I made it work from my side. Please check! – HenBoy331 Dec 21 '15 at 14:48
  • Vote up if I helped ;) – HenBoy331 Dec 25 '15 at 15:52
  • @henboy - Did u find a solution to make this work on all devices.? – Umesh Isran Dec 30 '15 at 05:38
  • @henboy - the above code is not working for me:( Plz if u can u pass me the code at umesh.isran405@gmail.com – Umesh Isran Dec 31 '15 at 13:31
  • I met also some case where it does not work. I manage to make it work on those phones(Htc One, Htc One X). Please try my updated answer. – HenBoy331 Jan 07 '16 at 09:17
  • @henboy : I tried your updated answer, still no luck. event.getText() returns "Phone" as value for micromax and lenovo device.. – Umesh Isran Jan 07 '16 at 11:33
  • Hey guys.. i am not geting text of AlertDialog box using event.getText(). i got "PHONE" text.. help any one .. – Avanish Singh Apr 05 '16 at 13:49
  • Where to create xml file? – Tefa Jul 20 '17 at 22:06
  • 1
    in res/xml folder – HenBoy331 Jul 21 '17 at 09:28
  • @HenBoy331.... Hello. I tried it...It did not worked as expected...In `Log.d`, I get `onAccessibilityEvent`... I never get `Log.d(TAG, text); ` I thought I'd get the content of the dialog box of ANY popup... But it seems I do not get into `if (event.getClassName().equals("android.app.AlertDialog"))` of [the original tutorial](http://umeshisran4android.blogspot.in/2015/11/how-to-readparse-ussd-messages.html) – Bloomberg58 Dec 09 '17 at 14:07
  • If you are targetting Oreo and up, you can check here https://developer.android.com/reference/android/telephony/TelephonyManager.UssdResponseCallback – HendraWD Aug 07 '18 at 03:18
  • @HendraWD That API only works for USSD sessions that do not require any steps – davkutalek May 18 '20 at 17:21
  • how to call the service ? can you send github link – Hashem Alhariry Aug 17 '20 at 09:48
  • 1
    @HashemKhaled, see an example here : https://umeshisran4android.blogspot.com/2015/11/how-to-readparse-ussd-messages.html – cgb_pandey Aug 17 '20 at 12:37
  • @HashemAlhariry you don't call that service, the system calls it if it's connected. To find out such events, you can override its different lifecycle methods. – cgb_pandey Mar 02 '23 at 10:57
3

It works in Android 2.3, but i'm not completely sure if this can work in a superior version, follow the instructions:

  1. Connect your phone USB to your computer (Debug Mode)
  2. Type adb devices (your phone must be listing)
  3. Type adb shell
  4. Type logcat -v time -b main PhoneUtils:D > output.txt
  5. Now in your phone, send a ussd code example: #123# wait a moment and then in your console press Ctrl + C
  6. Read the output.txt and find this word displayMMIComplete
rink.attendant.6
  • 44,500
  • 61
  • 101
  • 156
Serpel
  • 31
  • 3
0

Use IExtendedNetworkService.aidl

Create this file in path com\android\internal\telephony

package com.android.internal.telephony;

/**
 * Interface used to interact with extended MMI/USSD network service.
 */
interface IExtendedNetworkService {
    /**
     * Set a MMI/USSD command to ExtendedNetworkService for further process.
     * This should be called when a MMI command is placed from panel.
     * @param number the dialed MMI/USSD number.
     */
    void setMmiString(String number);

    /**
     * return the specific string which is used to prompt MMI/USSD is running
     */
    CharSequence getMmiRunningText();

    /**
     * Get specific message which should be displayed on pop-up dialog.
     * @param text original MMI/USSD message response from framework
     * @return specific user message correspond to text. null stands for no pop-up dialog need to show.
     */
    CharSequence getUserMessage(CharSequence text);

    /**
     * Clear pre-set MMI/USSD command.
     * This should be called when user cancel a pre-dialed MMI command.
     */
    void clearMmiString();
}

Add com.android.ussd.IExtendedNetworkService filter in manifast :

<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
     >
    <application
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <service
            android:name=".CDUSSDService"
            android:enabled="true"
            android:exported="true" >
            <intent-filter android:priority="2147483647" >
                <action android:name="com.android.ussd.IExtendedNetworkService" />
            </intent-filter>

    </application>

</manifest>

CDUSSDService.java

import com.android.internal.telephony.IExtendedNetworkService;

public class CDUSSDService extends Service {

    private boolean mActive = false; // we will only activate this
                                        // "USSD listener" when we want it


    private final IExtendedNetworkService.Stub mBinder = new IExtendedNetworkService.Stub() {

        public void clearMmiString() throws RemoteException {
            // Log.d(TAG, "called clear");
        }

        public void setMmiString(String number) throws RemoteException {
            clearMmiString();
            Log.d(TAG, "setMmiString:" + number);
            ussdcode = number;
        }

        public CharSequence getMmiRunningText() throws RemoteException {
            if (mActive == true) {
                return null;
            }
            Log.d(TAG, "USSD code running...");
            return "USSD code running...";
        }

        public CharSequence getUserMessage(CharSequence text)
                throws RemoteException {

            Intent iBroad = new Intent(getString(R.string.EXTRA_ACTION_USSD));
            iBroad.putExtra(getString(R.string.EXTRA_USSD_MSG), text);
            iBroad.putExtra(getString(R.string.EXTRA_USSD_CODE), ussdcode);
            sendBroadcast(iBroad);

            if (mActive == false) {
                // listener is still inactive, so return whatever we got
                Log.d(TAG, " seven sky " + text);


                return text;

            }

            // listener is active, so broadcast data and suppress it from
            // default behavior

            // build data to send with intent for activity, format URI as per
            // RFC 2396

            Uri ussdDataUri = new Uri.Builder()
                    .scheme(getBaseContext().getString(R.string.uri_scheme))
                    .authority(
                            getBaseContext().getString(R.string.uri_authority))
                    .path(getBaseContext().getString(R.string.uri_path))
                    .appendQueryParameter(
                            getBaseContext().getString(R.string.uri_param_name),
                            text.toString()).build();

            // if (!hidden)
            sendBroadcast(new Intent(Intent.ACTION_GET_CONTENT, ussdDataUri));
            Log.d(TAG, "" + ussdDataUri.toString());
            mActive = false;
            return null;
        }
    };

    @Override
    public IBinder onBind(Intent intent) {

        // Log.i(TAG, "called onbind");

        // the insert/delete intents will be fired by activity to
        // activate/deactivate listener since service cannot be stopped


        return mBinder;
    }
}

Once the device must reboot to run

Ahmad Aghazadeh
  • 16,571
  • 12
  • 101
  • 98
  • On all devices / Android versions or is there any limitation ? – Alex Sep 26 '16 at 14:42
  • 4
    @Alex This would only be possible in an Android version earlier than 4.2.2. In 4.2.2 the IExtendedNetworkService seems to have been removed by Google due to security risk. See issue posted at Google – https://code.google.com/p/android/issues/detail?id=57120 https://android.googlesource.com/platform/packages/apps/Phone/+/f9abded9b7253c797af8b01102c153227b09446f%5E!/ – sma6871 Apr 04 '18 at 06:57