Зачем
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
Powered by Facebook Comments
Здравствуйте!
Уточните, пожалуйста, как это сделать «не забудьте ему в манифесте указать искомый алиас IExtendedNetworkService)»
Не могу найти инфы о том как прописать алиас для сервиса
вопрос снимается
А я не понял насчет алиеса. Не работает не пойму где. IExtendedNetworkService прописать в name в Androidmanifest? У меня прописано USSDDumbExtendedNetworkService, так, как и называется сервис. Но при перезагрузке ничего не происходит. Сделал проверку на включение биндинга сервиса. И ловлю интент с Uri, чтобы поставить пришедшую строку в URI_PAR но ничего не происходит. USSD отправляю интентом(ACTION_CALL) прямо из главного активити.
Еще тяжело понять как работает связка телефон-интерфейс(мой)-сервис-(биндер(Stub))-мое_активити_main? Нужно ли как-то регистрировать ресивер в сервисе на экшн ACTION_INSERT и ACTION_DELETE?
Hi, can you give us the project please, i’m kind of noob and still stuggling to get this to work 😛
It is not working for me. What should go into the manifest. I will appreciate if you could give us an example. Thanks
Haha, shouldn’t you be charging for that kind of kngewldoe?!
Hey! I just wish to give an enormous thumbs up for the great data you’ve got right here on this post. I will probably be coming back to your weblog for extra soon.
«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
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 !
Rachel: like I’ve said, there’s a difference between being quiet/shy and snobbish. It’s all about how you react to the other person, whether you smile or make an effort to acknowledge the person.
It is not working for me. What should go into the manifest? I will appreciate if you could give us an example. Thanks
tnx for this article,
but i can’t create sample,
pls write an sample to manager ussd and get response.
tnx
You can download sources here: http://commandus.com/blog/?p=45. Please provide e-mail. Also, if you find any error in my code, please inform me.
Привет, Пожалуйста, пришлите мне пример кода? как я могу открыть ссылку, уважением
Уведомление: How to close/cancel/dismiss a System Dialog programatically (Android) | PHP Developer Resource
Вы можете скачать исходники из поста http://commandus.com/blog/?p=45 (просьба оставить e-mail)
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)
It’s ok if you are use a newest API, just remove them.
Уведомление: Complile error for android USSD code sevice | Jisku.com - Developers Network
Уведомление: Mobile phone check balance USSD? | Free Android Enthusiasts
Уведомление: Mobile phone check balance USSD? | Jisku.com - Developers Network
Уведомление: USSD service not working - feed99
Уведомление: Intercepting USSD calls in Android | Code Vault
Скажите как установить алиас в манифесте
Скачайте исходники из поста в блоге
Complete working code sample https://github.com/alaasalman/ussdinterceptor
Enjoy!
Уведомление: Using IExtendedNetworkService to get USSD response in Android video
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.
Мне кажется, что у вас, во-первых, продублирован код интерфейса, а, во-вторых, эклипс ругается на IExtendedNetworkService.Stub.
Извините, актуальна только часть вопроса про дублирование интерфейса.
Подскажите, как вы боретесь с ошибками, указывающих на @Override, в Eclipse при автоматической генерации IExtendedNetworkService.java из IExtendedNetworkService.aidl?
Надо поправить код в новых версиях.
How do you use this with Dual SIM devices?
Sorry, I have no idea.
Полезная статья. особенно для таких новичков как я) Воспльзуюсь советами.
Я так понял на Android 5.Х Уже не работает данный метод ?
Или я что то не то делаю ?
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 – Rogier van der Wel
Уведомление: 使用IExtendedNetworkService在Android中获取USSD响应 – FIXBBS
Уведомление: How to read USSD messages in android?
Уведомление: How to close/cancel/dismiss a System Dialog programmatically (Android)