Prevent USSD dialog and read USSD response?
Asked Answered
H

3

22

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
Huddleston answered 27/2, 2014 at 2:6 Comment(9)
Have you got the solution ?Raman
not yet, does you have any idea for this problem?Huddleston
No i am just trying and working around but no +ve progress.Raman
please response in this topic whenever you found solution :)Huddleston
Ya sure I'll do Jack.Raman
Look at this page: code.google.com/p/android/issues/detail?id=1285Sarmentum
@HamidFzM in android > 4.2, we make USSD dial like thisHuddleston
Check out the answer here #6516406Ideography
Have you find the solution..??Prickle
I
43

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).

Invitation answered 7/12, 2015 at 11:40 Comment(18)
This works perfect on lollipop, but no success on 4.2.2 and 4.0.3.?Amino
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..Amino
Yes on some devices it does not work. On some devices it is com.android.phone.UssdAlertActivity instead of android.app.AlertDialogInvitation
Please check my new answer. I made it work from my side. Please check!Invitation
Vote up if I helped ;)Invitation
@henboy - Did u find a solution to make this work on all devices.?Amino
@henboy - the above code is not working for me:( Plz if u can u pass me the code at [email protected]Amino
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.Invitation
@henboy : I tried your updated answer, still no luck. event.getText() returns "Phone" as value for micromax and lenovo device..Amino
Hey guys.. i am not geting text of AlertDialog box using event.getText(). i got "PHONE" text.. help any one ..Alinaaline
Where to create xml file?Shericesheridan
in res/xml folderInvitation
@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 tutorialElielia
If you are targetting Oreo and up, you can check here developer.android.com/reference/android/telephony/…Nyssa
@Nyssa That API only works for USSD sessions that do not require any stepsNoisette
how to call the service ? can you send github linkSoutheastward
@HashemKhaled, see an example here : umeshisran4android.blogspot.com/2015/11/…Costume
@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.Costume
B
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
Bonham answered 18/5, 2015 at 19:30 Comment(0)
L
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

Livia answered 20/5, 2016 at 6:0 Comment(2)
On all devices / Android versions or is there any limitation ?Waxman
@Waxman 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 – code.google.com/p/android/issues/detail?id=57120 android.googlesource.com/platform/packages/apps/Phone/+/…Hurless

© 2022 - 2024 — McMap. All rights reserved.