Simple USSD request in Android 4.0+

  • Tutorial
There is still no API for USSD requests in Android. The bug has been hanging for 6 years !
I found different ways to create and retrieve information from USSD requests, but in the end, none were fine.
Then I found mentions that with the help of the special services updated in Android 4.0. Opportunities you can easily get the contents of the windows and so get the text from the window and the result of the USSD request. I tried it - it turns out fine! No reboots and reliable.

Request Submission

First, send the USSD request itself. It is quite simple:

String encodedHash = Uri.encode("#");
String ussd = "*100" + encodedHash;
startActivityForResult(new Intent("android.intent.action.CALL",
Uri.parse("tel:" + ussd)), 1);

To get permission to work with the phone, you need to get permission, for this we register in Android.Manifest.xml:

<uses-permission android:name="android.permission.CALL_PHONE" />

OK, the request is sent from the program, now you have to work hard to get its text.

Accessibility service

To work with special. Opportunities need to export our service. To do this, add to Android.Manifest.xml:

<service 
      android:name=".USSDService"
      android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" >
	<intent-filter>
   	<action android:name="android.accessibilityservice.AccessibilityService" />
	</intent-filter>
</service>

Instead of USSDService write the name of your class.

I took the class itself almost unchanged from here .

In order not to get too much, we change the onServiceConnected method:

protectedvoidonServiceConnected(){
    super.onServiceConnected();
    Log.v(TAG, "onServiceConnected");
    AccessibilityServiceInfo info = new AccessibilityServiceInfo();
    info.flags = AccessibilityServiceInfo.DEFAULT;
    info.packageNames = new String[]
            {"com.android.phone"};
    info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
    setServiceInfo(info);
}

You will also need an additional filter in the onAccessibilityEvent method:

publicvoidonAccessibilityEvent(AccessibilityEvent event){
    String text = getEventText(event);
    Log.v(TAG, String.format(
            "onAccessibilityEvent: [type] %s [class] %s [package] %s [time] %s [text] %s",
            getEventType(event), event.getClassName(), event.getPackageName(),
            event.getEventTime(), getEventText(event)));
    if (event.getClassName().equals("android.app.AlertDialog")) {
		performGlobalAction(GLOBAL_ACTION_BACK);
    		Log.i(TAG, text);
		Intent intent = new Intent("REFRESH");
		intent.putExtra("message", text);
		sendBroadcast(intent);
    }
}

The performGlobalAction (GLOBAL_ACTION_BACK) method requires Android 4.1+, if you don’t use it, you can keep it in 4.0. It closes the window immediately after the appearance of AlertDialog, so the window will not remain hanging.
For simplicity, I have already added the sendBroadcast method to send the received message further.
To receive a message, add the following methods to the desired class:

private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
  @OverridepublicvoidonReceive(Context context, Intent intent){
    String message = intent.getStringExtra("message");
    Log.i("receiver", "Got message: " + message);
    showText(message);
  }
};

showText is your procedure that does something with the received text.

For BroadcastReceiver to work, you need to add some code to onCreate () or a similar method of your class:

IntentFilter mFilter = new IntentFilter("REFRESH");
mContext.registerReceiver(mMessageReceiver, mFilter);
isRegistered = true;

And this is in onPause () or similar:

try
{
    if (isRegistered) {
        mContext.unregisterReceiver(mMessageReceiver);
        isRegistered = false;
    }
}
catch (Exception e) {
    e.printStackTrace();
}

That's all! As you can see, simple and reliable.

Underwater rocks

There may be problems with working in the background: for example, if the screen is locked, then the AccessibilityService will not work at all - you need to wake the device. In the unlocked state, the request will always come to the fore, which is also not convenient.

AccessibilityService will listen all the time, even when the user dials the USSD code himself or, even worse, if com.android.phone throws AlertDialog. So you need to either strengthen the filters (for example, parse only if the message has a certain sequence), or use the flag to process events only if your application has made a USSD request.

And do not forget in the settings special. opportunities to activate your application, by the way for this, it would also be nice to add a% check)

Alternatives:
habrahabr.ru/post/130717- takes the answer from the logs. It is inconvenient that in order to suppress a window you have to resort to hacks, and to do this through AccessibilityService is cleaner and easier.
github.com/alaasalman/ussdinterceptor is still an old way. Uses an undocumented class. According to reviews, it often falls off and requires a reboot.

Also popular now: