防止USSD对话框弹出并读取USSD响应?

21

我对USSD进行了很多研究,但是我无法阅读USSD响应,并且不知道如何防止像这个应用程序那样的USSD对话框。

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

我安装了这个https://github.com/alaasalman/ussdinterceptor,重启了我的手机(S2 Android 4.0.3),发送USSD代码但没有反应,有人告诉我要读取日志,但我该怎么做呢?
我尝试使用以下代码从日志中读取USSD。

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);
    }

}

输出结果大致如下:
com.sec.android.app.callsetting.allcalls:com.sec.android.callsetting.allcalls.AllCallsProvider Terminated ALSA PLAYBACK device hifi

你有解决方案吗? - Wilson
找到解决方案后,请在此主题中回复 :) - Jack
请查看此页面:http://code.google.com/p/android/issues/detail?id=1285 - hamidfzm
@HamidFzM 在 Android > 4.2 中,我们可以这样拨打 USSD。 - Jack
你找到解决方案了吗..? - Harry Sharma
显示剩余4条评论
3个回答

42
可以使用辅助功能服务来实现。 首先创建一个服务类:
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);
    }
}

在Android清单文件中声明它

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

创建一个描述名为"ussd_service"的可访问性服务的xml文件。
<?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" />

就这样。一旦应用程序安装完成,您必须在可访问性设置中启用服务(设置 -> 可访问性设置 -> 您的应用程序名称)。

解决方案描述在这里在这里(俄语)


2
是的,在某些设备上它不起作用。在一些设备上,它是 com.android.phone.UssdAlertActivity 而不是 android.app.AlertDialog。 - HenBoy331
1
请检查我的新答案。我已经在我的这一端使它工作了。请检查! - HenBoy331
1
在res/xml文件夹中。 - HenBoy331
@HenBoy331.... 你好。我尝试了一下,但结果并不如预期。在 Log.d 中,我得到了 onAccessibilityEvent。我从未得到过 Log.d(TAG, text);。我以为我可以获取任何弹出窗口的对话框内容...但似乎我没有进入 [原始教程](http://umeshisran4android.blogspot.in/2015/11/how-to-readparse-ussd-messages.html) 中的 if (event.getClassName().equals("android.app.AlertDialog")) - Bloomberg58
1
@HashemKhaled,这里有一个例子:https://umeshisran4android.blogspot.com/2015/11/how-to-readparse-ussd-messages.html - cgb_pandey
显示剩余13条评论

3

这在Android 2.3中可以工作,但我不完全确定它是否适用于更高版本,请按照以下说明操作:

  1. 将手机USB连接到计算机上(开启调试模式)
  2. 输入adb devices(您的手机必须被列出)
  3. 输入adb shell
  4. 输入logcat -v time -b main PhoneUtils:D > output.txt
  5. 现在在您的手机上发送一个ussd代码示例:#123# 等待一会儿,然后在控制台中按下Ctrl + C
  6. 阅读output.txt并找到单词displayMMIComplete

0
请使用IExtendedNetworkService.aidl文件。
在路径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();
}

在 manifest 文件中添加 com.android.ussd.IExtendedNetworkService 过滤器:
<?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;
    }
}

一旦设备必须重新启动才能运行


所有设备/Android版本都支持吗?还是有限制? - Alex
4
@Alex,这只有在Android 4.2.2之前的版本中才可能实现。在4.2.2中,由于安全风险,Google似乎已经删除了IExtendedNetworkService。请参阅Google发布的问题 - https://code.google.com/p/android/issues/detail?id=57120https://android.googlesource.com/platform/packages/apps/Phone/+/f9abded9b7253c797af8b01102c153227b09446f%5E!/ - sma6871

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接