Android 裝逼技術之暗碼啟動應用
阿新 • • 發佈:2019-07-15
什麼是暗碼?
在撥號盤中輸入*#*#<code>#*#*
後,APP 可以監控到這些輸入,然後做相應的動作,比如啟動應用,是不是有點騷。
下面看下這個騷操作是如何實現的。
效果預覽
原始碼
DialtactsActivity#showDialpadFragment
DialtactsActivity 中有個 showDialpadFragment 方法,用來載入顯示撥號盤,因此入口就從 showDialpadFragment 看起,基於 Android P 分析。
private void showDialpadFragment(boolean animate) { //…… final FragmentTransaction ft = getFragmentManager().beginTransaction(); if (dialpadFragment == null) { dialpadFragment = new DialpadFragment(); ft.add(R.id.dialtacts_container, dialpadFragment, TAG_DIALPAD_FRAGMENT); } else { ft.show(dialpadFragment); } //…… }
具體實現在 DialpapFragment 中,看到 DialpapFragment 實現了 TextWatcher,TextWatcher 有 3 個重要方法,分別為:beforeTextChanged,onTextChanged 和 afterTextChanged,重點看 afterTextChanged 方法。
DialpadFragment#afterTextChanged
public class DialpadFragment extends Fragment implements View.OnClickListener, View.OnLongClickListener, View.OnKeyListener, AdapterView.OnItemClickListener, TextWatcher, PopupMenu.OnMenuItemClickListener, DialpadKeyButton.OnPressedListener { //…… @Override public void afterTextChanged(Editable input) { // When DTMF dialpad buttons are being pressed, we delay SpecialCharSequenceMgr sequence, // since some of SpecialCharSequenceMgr's behavior is too abrupt for the "touch-down" // behavior. if (!digitsFilledByIntent && SpecialCharSequenceMgr.handleChars(getActivity(), input.toString(), digits)) { // A special sequence was entered, clear the digits digits.getText().clear(); } if (isDigitsEmpty()) { digitsFilledByIntent = false; digits.setCursorVisible(false); } if (dialpadQueryListener != null) { dialpadQueryListener.onDialpadQueryChanged(digits.getText().toString()); } updateDeleteButtonEnabledState(); } //…… }
這裡呼叫了 SpecialCharSequenceMgr 輔助工具類的 handleChars 方法,看這個方法。
SpecialCharSequenceMgr#handleChars
public static boolean handleChars(Context context, String input, EditText textField) { // get rid of the separators so that the string gets parsed correctly String dialString = PhoneNumberUtils.stripSeparators(input); if (handleDeviceIdDisplay(context, dialString) || handleRegulatoryInfoDisplay(context, dialString) || handlePinEntry(context, dialString) || handleAdnEntry(context, dialString, textField) || handleSecretCode(context, dialString)) { return true; } if (MotorolaUtils.handleSpecialCharSequence(context, input)) { return true; } return false; }
handleChars 方法中,會對各種特殊的 secret code 進行匹配處理,這裡我們看 handleSecretCode。
SpecialCharSequenceMgr#handleSecretCode
static boolean handleSecretCode(Context context, String input) {
// Secret code specific to OEMs should be handled first.
if (TranssionUtils.isTranssionSecretCode(input)) {
TranssionUtils.handleTranssionSecretCode(context, input);
return true;
}
// Secret codes are accessed by dialing *#*#<code>#*#* or "*#<code_starting_with_number>#"
if (input.length() > 8 && input.startsWith("*#*#") && input.endsWith("#*#*")) {
String secretCode = input.substring(4, input.length() - 4);
TelephonyManagerCompat.handleSecretCode(context, secretCode);
return true;
}
return false;
}
再看下 TelephonyManagerCompat.handleSecretCode 方法。
TelephonyManagerCompat#handleSecretCode
public static void handleSecretCode(Context context, String secretCode) {
// Must use system service on O+ to avoid using broadcasts, which are not allowed on O+.
if (BuildCompat.isAtLeastO()) {
if (!TelecomUtil.isDefaultDialer(context)) {
LogUtil.e(
"TelephonyManagerCompat.handleSecretCode",
"not default dialer, cannot send special code");
return;
}
context.getSystemService(TelephonyManager.class).sendDialerSpecialCode(secretCode);
} else {
// System service call is not supported pre-O, so must use a broadcast for N-.
Intent intent =
new Intent(SECRET_CODE_ACTION, Uri.parse("android_secret_code://" + secretCode));
context.sendBroadcast(intent);
}
}
可以看到在撥號中接收到*#*#<code>#*#*
這樣的指令時,程式會對外發送廣播,這就意味著我們能夠接收這個廣播然後可以做我們想做的事情。
接下來我們看看這個接受廣播程式碼是怎麼寫。
應用
首先在 AndroidManifest 檔案中註冊廣播接收器。
<receiver
android:name=".SecretCodeReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SECRET_CODE" />
<data android:scheme="android_secret_code" android:host="1010" />
</intent-filter>
</receiver>
接收廣播,啟動應用。
public class SecretCodeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent != null && SECRET_CODE_ACTION.equals(intent.getAction())){
Intent i = new Intent(Intent.ACTION_MAIN);
i.setClass(context, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
}
這樣只要在撥號中輸入*#*#1010#*#*
就能啟動相應的應用程式,OK,收功