问题
How to open the Android ServiceMode menu programmatically on Samsung Phones ?
Manually, I can do it by dialing the ussd code *#0011#.
回答1:
This was a challenge and I've been a few hours looking for a solution. But i am afraid i don't have good news.
1. First attemp, Intent.ACTION_DIAL
Is true that in the beginning, it was possible to directly call USSD codes from an app (using the intent Intent.ACTION_DIAL), and even from a website, just using the "tel:" schema. That opens the dialer, an puts the desired number. As when you put the last #, the code get send automatically, it was almost transparent to the user, whose interaction wasn't required. But that was actually considered a vulnerability of the system, since somebody could write malicious software, or even more, insert malicious code in a website that could even wipe your phone or block the sim card. You can read about this, for example in this link As of September 2012, it seems that Samsung finally fixed that vulnerability so for instance your S3 is not capable of that. At this point it will be hard to find any device still vulnerable.
2. Second Attempt, the workaround
Ok everything in Android is an app, even the phone itself is an app. So, why couldn't we try to replicate its code? And actually, what happens when the user manually sends a ussd? There was too possible answers:
- The phone sends the code to the provider and waits for a response, or
- The phone does something locally
Easy to test: I put my phone in flight mode, so if it has to send a code, hopefully it wont be able to. It was as i expected: i dialed the code, and the ServiceMode screen appeared! And it was empty, so we can say that the dialer opens a system application, and this application calls the provider.
Its easy to find out which is this application. In Samsung devices, at least in S4, its name is ServiceMode. You can find it at Settings > Application manager > All
. So we can try to find out how it is launched more by means of one of this:
2.1. Reading the phone application code
I tried, but it was pretty confusing at the beginning, so i went to option 2
2.2. Checking the Service mode app
I opened ES file explorer > app manager. connected the phone, and in phone/backups/apps i had the servicestatus apk. a few minutes or inverse engineering after, i had the manifest file, as revealing as it uses to be. There i could see a bunch of activities, and also a few broadcast receivers. I didn't know what i was more afraid of.
2.2.1 Lets try with the activities.
Since we know we can open an specific application from another, i will try to write a simple app, that open one of this activities. This is as simple as:
Intent i= new Intent(Intent.Action_MAIN);
i.setClass( "package name", "class name");
startActivity(i);
Since this app is not meant to be open from a launcher icon, it has not an activity with an intent filter that let us know that it is the main activity. So i have to try at least a few of them. But anyways, i saw something i didn't like. I will tell you later.
I tried, but, as i expected, i cannot open that activities. I get an activity not found exception. So, lets try with a receiver. At least this will be fun.
2.2.2 Lets try with the receivers.
There is a few, but specially i liked one with this intent-filter:
<intent-filter>
<action android:name="android.provider.Telephony.SECRET_CODE" />
...
Looks good. I went yo Telephony.java, and there i could see this:
/**
* Broadcast Action: A "secret code" has been entered in the dialer. Secret codes are
* of the form *#*#<code>#*#*. The intent will have the data URI:</p>
*
* <p><code>android_secret_code://<code></code></p>
*/
public static final String SECRET_CODE_ACTION =
"android.provider.Telephony.SECRET_CODE";
Looks great, its an specific intent, probably the schema we need is not "tel:" but "android_secret_code://" and probably the codes has to be in a different internal format. I tried, more or less just for fun, since i knew that there is the same problem i saw in the activities, and is this:
<permission android:name="com.sec.android.app.servicemodeapp.permission.KEYSTRING" android:protectionLevel="signatureOrSystem" />
The app declares this permission, and every component uses it. so, or you know how to declare and set a signature, or you are a system app. Otherwise you cannot interact with this app at all.
Of course you couldn't even think about creating your own servicemode app, since you wouldn't perform that kind of communication with the provider without being a system app (since you have to pass through some firmware apps that will say a sad "who do you think you are")
Conclusions
There is nothing we can do at his point, at least with a Samsung phone, and we can think every other phone will be the same.
Alternatives
Well, actually none really good. But lets see:
Alternative 1. Find another kind of ussd code
We know that there is 2 kind of USSD. The ones that when you dial the last #, the action is automatically triggered, and the other, which require that you press the call button.
If you try with a third-party dialer, you can see that
- whe you dial the first type kind of code, nothing is triggered at all (only the firmware dialer can trigger them)
- you can use the other kind (type code + press call). I suppose this means that this kind of codes are less risky, since doesn't change anything in the device, so they are allowed.
- of course, if you type a code of the first type and press call, you get an error because that code doesn't work that way.
So, if a third-party can send that type of codes, every app could. So If you could try to find an alternative ussd code, of that type that are triggered after the usser press the call button, you could achieve your what you need. In that case, you should use an Intent.ACTION_CALL intent
//if you use the ACTION_DIAL intent, it open the dialer and put the given number in it. //meanwhile, if we use the ACTION_CALL, the app will directly call.
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:"+ "*" + Uri.encode("#") + "0011" + Uri.encode("#"))); //here you woud put your alternative code
startActivity(intent);
Also you would need to declare this permission in your manifest
<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
Probably you could find an alternative code to do this, since i ve seen a few in the internet, but at this point no one worked for me. You can give it a try
Alternative 2. Wait for an official api to come up
In the android project page at coogle code, there is a request to Add USSD API support. It has many subscribers, but it is quite old (more than 5 years), i'm afraid that's not really good. Anyways, that could happen, probably the android tem will consider to prepare an api so an app could declare some permissions, and make requests of some kind of codes. But, I know, this can be waiting forever.
So, what to say. Would luck looking for a solution, or for an alternative to your app. I'm sorry my research wasn't more fruitful :(
回答2:
I think that I got a solution.
By running the bellow code on a rooted phone you should open the Samsung ServiceModeApp (the service mode shown in this post). Programmatically, I think that one can run anything if logged as root. However, there are issues to face such as:
- Rooted phone. If you're creating apps for publishing, consider that less than 2% of all Android devices are rooted;
- Very specific application. This bellow code runs in a specific version of Samsung device (I've run it in a Galaxy SIII GT-9300). It may not run in other models, and surelly will not in other brands. You must re-implement each case.
Repare that 0011 is the argument that you must pass to the activity. Code follows:
PackageManager packageManager = this.getPackageManager();
if (packageManager != null) {
measure_intent = new Intent(Intent.ACTION_MAIN).
addCategory(Intent.CATEGORY_LAUNCHER).setComponent(new ComponentName(
"com.sec.android.app.servicemodeapp",
"com.sec.android.app.servicemodeapp.ServiceModeApp"));
measure_intent.putExtra("keyString", "0011");
ResolveInfo resolved = packageManager.resolveActivity(
measure_intent, PackageManager.MATCH_DEFAULT_ONLY);
if (resolved != null) {
startActivity(measure_intent);
return;
} else {
Toast.makeText(this, "version not implemented", Toast.LENGTH_LONG).show();
}
}
I've reached to it by pulling ServiceModeApp from Android's /system/app directory, then decompiling the .apk file, and then analyzing its code. Hope it helps! :)
回答3:
FWIW: ServiceMode (SM) access is both HW and SW dependent, and every manufacturer tries to repeatedly hide the SM functionality on every iteration of the AOS API. The SM is actually located in modem FW and what you see is just the Java wrapper making and presenting the results of the various calls to the modem RTOS. (You can check in your modem FW to see for your self.) In addition, with recent Samsung/HTC stock FW, they also require enabling certain files in the /efs/ directory.
回答4:
Not really a fix, but using Intent.ACTION_DIAL you can display the code in the dial window (hash star hash star 4636 star hash star hash), then long press and cut, then long press and paste does the trick (on Moto G5s+ anyway). This is still simpler for me to do it from my app than bringing up separate dialer, but as I say, not really a fix.
来源:https://stackoverflow.com/questions/20248195/open-servicemode-menu-programmatically-in-android