Как обрабатывать в Андроиде USSD/MMI запросы

Зачем

USSD/MMI запросы- это те коды, которые можно набирать на номеронабирателе телефона, начинающиеся со звездочки («*») и заканчивающиеся решеткой («#»), чтобы получить, например баланс счета или код IMEI телефона.

Запросы еще нужны, если вы хотите вшить какие нибудь «инженерные» коды, навроде пасхальных яиц в номеронабиратель.

Проблема

Много лет висит запрос фичи разработчикам Андроида на включение API, которое бы позволило отправлять USSD запросы и затем их обрабатывать. Под запросом уже собрано несколько сот голосов разработчиков- изменений пока нет.

Не так давно появилось классное приложение для проверки баланса USSD Checker, которое периодически проверяет баланс и красиво отображает затраты на связь.

Для этого нужно сделать звонок на номер вроде этого: *100#. С этим никаких проблем нет. Но: приложение должно получить ответ на USSD запрос. С этим закавыка- Андроид сам создает диалог и показывает ответ, оставляя ваше приложениев неведении. Если хотите разобраться, как все таки получить ответ в отсутствие API, это статья для вас.

Замечание: поскольку это недокументированный способ, он может не работать на всех телефонах, если в референсную систему разработчиком телефона внесены изменения.

 

Как обрабатывать USSD запросы

В пакете package com.android.phone класс PhoneUtils, загружаемый как часть Андроида, связанного с телефонией, пытается установить связь с сервисом com.android.ussd.IExtendedNetworkService.

На тех телефонах, что я пробовал, такого сервиса не установлено. Тогда Андроид просто показывает тот самый диалог с полученным ответом. Если же сервис есть, он запускается и делаются вызовы, которые и позволят нам из сервиса увидеть, какой ответ получен.

Я расскажу, как я написал небольшое приложение call me back, которое есть в маркете, для отправки USSD запроса в сервис «Перезвони мне». Такой сервис есть у многих операторов, и это бесплатно- иначе в чем же смысл ;).

К тому же, это, пожалуй, единственный способ, как это сделать на Java без необходимости рутить телефон. Недостаток этого кроется в том, что телефон после установки приложения придется перезагрузить. Это потому, что сшивание с сервисом происходит только в момент создания экземпляра класса PhoneUtils- а именно, при включении телефона.

Ниже приведен aidl файл IExtendedNetworkService.aidl, который нужно включить в проект Eclipse.

 

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 timeout message and pre-set MMI/USSD command
     * This should be called when user cancel a pre-dialed MMI command.
     */
    void clearMmiString();
}

/**
 *  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 timeout message and pre-set MMI/USSD command
     * This should be called when user cancel a pre-dialed MMI command.
     */
    void clearMmiString();
}

 

И по нему создадать свой сервис (не забудьте ему в манифесте указать искомый алиас IExtendedNetworkService):

 

package com.commandus.ussd;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.IBinder;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.util.Log;

import com.android.internal.telephony.IExtendedNetworkService;
import com.commandus.callmeback.R;

/**
 * Service implements IExtendedNetworkService interface.
 * USSDDumbExtendedNetworkService
 * Service must have name "com.android.ussd.IExtendedNetworkService" of the intent declared
 * in the Android manifest file so com.android.phone.PhoneUtils class bind
 * to this service after system rebooted.
 * Please note service is loaded after system reboot!
 * Your application must check is system rebooted.
 * @see Util#syslogHasLine(String, String, String, boolean)
 */
public class USSDDumbExtendedNetworkService extends Service {
	public static final String TAG = "CommandusUSSDExtNetSvc";
	public static final String LOG_STAMP = "*USSDTestExtendedNetworkService bind successfully*";
	public static final String URI_SCHEME = "ussd";
	public static final String URI_AUTHORITY = "commandus.com";
	public static final String URI_PATH = "/";
	public static final String URI_PAR = "return";
	public static final String URI_PARON = "on";
	public static final String URI_PAROFF = "off";
	public static final String MAGIC_ON = ":ON;)";
	public static final String MAGIC_OFF = ":OFF;(";
	public static final String MAGIC_RETVAL = ":RETVAL;(";

	private static boolean mActive = false;
	private static CharSequence mRetVal = null;
	private Context mContext = null;
	private String msgUssdRunning = "xxx..."; 

    final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
        	if (Intent.ACTION_INSERT.equals(intent.getAction())) {
        		mContext = context;
        		if (mContext != null) {
        			msgUssdRunning = mContext.getString(R.string.msgRunning);
        			mActive = true;
        			Log.d(TAG, "activate");
        		}
            } else if (Intent.ACTION_DELETE.equals(intent.getAction())) {
            	mContext = null;
            	mActive = false;
            	Log.d(TAG, "deactivate");
            }
        }
    };

    private final IExtendedNetworkService.Stub mBinder = new IExtendedNetworkService.Stub() {
		@Override
		public void setMmiString(String number) throws RemoteException {
			Log.d(TAG, "setMmiString: " + number);
		}

		@Override
		public CharSequence getMmiRunningText() throws RemoteException {
			Log.d(TAG, "getMmiRunningText: " + msgUssdRunning);
			return msgUssdRunning;
		}

		@Override
		public CharSequence getUserMessage(CharSequence text)
				throws RemoteException {
			if (MAGIC_ON.contentEquals(text)) {
				mActive = true;
				Log.d(TAG, "control: ON");
				return text;
			} else {
				if (MAGIC_OFF.contentEquals(text)) {
					mActive = false;
					Log.d(TAG, "control: OFF");
					return text;
				} else {
					if (MAGIC_RETVAL.contentEquals(text)) {
						mActive = false;
						Log.d(TAG, "control: return");
						return mRetVal;
					}
				}
			}

			if (!mActive) {
				Log.d(TAG, "getUserMessage deactivated: " + text);
				return text;
			}
			String s = text.toString();
			// store s to the !
			Uri uri = new Uri.Builder()
				.scheme(URI_SCHEME)
				.authority(URI_AUTHORITY)
				.path(URI_PATH)
				.appendQueryParameter(URI_PAR, text.toString())
				.build();
			sendBroadcast(new Intent(Intent.ACTION_GET_CONTENT, uri));
			mActive = false;
			mRetVal = text;
			Log.d(TAG, "getUserMessage: " + text + "=" + s);
			return null;
		}

		@Override
		public void clearMmiString() throws RemoteException {
			Log.d(TAG, "clearMmiString");
		}
    };

    /**
     * Put stamp to the system log when PhoneUtils bind to the service
     * after Android has rebooted. Application must call {@link Util#syslogHasLine(String, String, String, boolean)} to
     * check is phone rebooted or no. Without reboot phone application does not bind tom this service!
     */
	@Override
	public IBinder onBind(Intent intent) {
		// Do not localize!
		Log.i(TAG, LOG_STAMP);
		IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_INSERT);
        filter.addAction(Intent.ACTION_DELETE);
        filter.addDataScheme(URI_SCHEME);
        filter.addDataAuthority(URI_AUTHORITY, null);
        filter.addDataPath(URI_PATH, PatternMatcher.PATTERN_LITERAL);
		registerReceiver(mReceiver, filter);

		return mBinder;
	}

	public IBinder asBinder() {
		Log.d(TAG, "asBinder");
		return mBinder;
	}

}

 

Из кода видно, сервис в журнал делает отметку: «сервис запущен». Остается в GUI коде проверить, был ли сервис запущен, иначе говоря, был ли Андроид перезапущен (а иначе мы не дождемся ответа).

И последнее, так как сервис запускается сам, то из сервиса в GUI передача событий делается посылкой интентов. В активностях нужно зарегистрировать их класс- получатель.

В заключение, чтение исходников Андроида наталкивает на мысль- и все таки это работает.

 

Comments

comments

Powered by Facebook Comments

Как обрабатывать в Андроиде USSD/MMI запросы: 35 комментариев

  1. const

    Здравствуйте!
    Уточните, пожалуйста, как это сделать «не забудьте ему в манифесте указать искомый алиас IExtendedNetworkService)»
    Не могу найти инфы о том как прописать алиас для сервиса

    1. Иван

      А я не понял насчет алиеса. Не работает не пойму где. IExtendedNetworkService прописать в name в Androidmanifest? У меня прописано USSDDumbExtendedNetworkService, так, как и называется сервис. Но при перезагрузке ничего не происходит. Сделал проверку на включение биндинга сервиса. И ловлю интент с Uri, чтобы поставить пришедшую строку в URI_PAR но ничего не происходит. USSD отправляю интентом(ACTION_CALL) прямо из главного активити.
      Еще тяжело понять как работает связка телефон-интерфейс(мой)-сервис-(биндер(Stub))-мое_активити_main? Нужно ли как-то регистрировать ресивер в сервисе на экшн ACTION_INSERT и ACTION_DELETE?

      1. car insurance

        «Human pygmies do not appear to have evolved through positive selection for small stature—this was a by-product of selection for early onset of reproduction.» mean Greco-Roman IQ may have been only about 90 or so. This would be in line with Greg Cochran’s thinking: mean IQs in the 100+ range seem to be a recent evolutionary Wouldn’t this put the the IQ in parts of

      2. http://www.insuranceprices.dynddns.us/

        c’est uoi un professionnel de l’avaiation civile, MELO le rigolo de service, un délégué syndical, un ingenieur de la DGAC, un motoriste…..? Toujours aussi nuls tes commentaires, le modéle AF est MORT, sa gestion est catastrophique, ses syndicats sont archaïques, bientôt Pôle emploi…. Surtout pour les charlots comme toi !

  2. Уведомление: How to close/cancel/dismiss a System Dialog programatically (Android) | PHP Developer Resource

  3. Ahmadreza

    Hi
    tank for your work
    but when i import this code to my eclipse code i recive many errors that said to me that any @Overrided functions in USSDDumbExtendedNetworkService.java file is incorrect. also when is use eclipse to override IExtendedNetworkService.Stub functions there is no functions such setMmiString, getMmiRunningText, getUserMessage to override!!
    i use ADT20.0, elipse juno and android2.3.3 platform.
    Also i check your project call me back and see all of these errors.

    (Sorry for my bad english)

  4. Уведомление: Complile error for android USSD code sevice | Jisku.com - Developers Network

  5. Уведомление: Mobile phone check balance USSD? | Free Android Enthusiasts

  6. Уведомление: Mobile phone check balance USSD? | Jisku.com - Developers Network

  7. Уведомление: USSD service not working - feed99

  8. Уведомление: Intercepting USSD calls in Android | Code Vault

  9. Уведомление: Using IExtendedNetworkService to get USSD response in Android video

  10. ttotto

    Thank you for sharing.
    I tried turn off and turn on my android phone after running this sample code, but I always get msgNeedReboot alert.
    Could you let me know why this occurs? I am looking forward your help.
    Thanks again.

  11. Julia

    Мне кажется, что у вас, во-первых, продублирован код интерфейса, а, во-вторых, эклипс ругается на IExtendedNetworkService.Stub.

      1. dmitriy

        Подскажите, как вы боретесь с ошибками, указывающих на @Override, в Eclipse при автоматической генерации IExtendedNetworkService.java из IExtendedNetworkService.aidl?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *