Android对于暗码功能有两种思路:
第一种:可以在原生的Dialer模块中截取输入的 字符串,进行match,然后启动指定的Activtiy;
好处:直接在Dialer中进行截取,通过startActivity进行启动,反应快,且暗码可以自由定义(不过一般要以*#或者#*开头)。
坏处:只能使用带源码的Dialer才会有效,使用第三方的拨号界面将无效。
第二种:通过Android通用"####"的机制。
好处:任意拨号盘输入此暗码,都能生效;
坏处:通过广播的机制进行响应,有可能延迟(Marvell1908平台出现过该类问题),必须要将该进程添加到System组(在源码中编译,或手动进行签名)。
对于第一种方式大家都会,直接拦截拨号盘就OK了。
对于第二种方式,目前我们主要是针对使用google dialer的项目进行使用:
首先看下Phone中对暗码的处理:
packages/services/telephony/src/com/android/phone/SpecialCharSequenceMgr.java::handleSecretCode(Context , String)
/**
* Handles secret codes to launch arbitrary activities in the form of *#*#<code>#*#*.
* If a secret code is encountered an Intent is started with the android_secret_code://<code>
* URI.
*
* @param context the context to use
* @param input the text to check for a secret code in
* @return true if a secret code was encountered
*/
static private boolean handleSecretCode(Context context, String input) {
// Secret codes are in the form *#*#<code>#*#*
int len = input.length();
if (len > 8 && input.startsWith("*#*#") && input.endsWith("#*#*")) {
Intent intent = new Intent(TelephonyIntents.SECRET_CODE_ACTION,Uri.parse("android_secret_code://" + input.substring(4, len - 4)));
context.sendBroadcast(intent);
return true;
}
return false;
}
在Phone中会截取暗码,然后将暗码封装到一条Broadcast中,通过TelephonyIntents.SECRET_CODE_ACTION 发送出去。
我们来看下
frameworks/base/telephony/java/com/android/internal/telephony/TelephonyIntents.java
/**
* 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";
该action携带的data为 android_secret_code://
通用的暗码方案,就是通过接收这条广播来实现的。
实现方法:
添加AndroidManifest.xml
<receiver android:name=".MyReceiver">
<intent-filter >
<action android:name="android.provider.Telephony.SECRET_CODE"/>
<data android:scheme="android_secret_code" android:host="906"/> <!-- *#*#906#*#* -->
</intent-filter>
</receiver>
注意:android:host="906"中的906可以任意修改,修改成自己需要的。比如修改成999,拨号盘则需要输入*##999##*
注册BroadcastReceiver:
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if("android.provider.Telephony.SECRET_CODE".equals(action)){
String host = (intent.getData() == null) ? null : intent.getData().getHost();
}
}
注意:在合入Google Dialer后,会出现上述操作失效的问题,反编译Google Dialer源代码发现,log中走了这一条:cwv.c(“TelephonyManagerCompat.handleSecretCode”, “not default dialer, cannot send special code”, new Object[0]);,即需要把Dialer设置成默认的Dialer。
因此我们需要继续修改以下方法:
packages\services\Telecomm\res\values\config.xml
<!-- Determines if the current device should allow emergency numbers to be logged in the
call log. Some carriers require that emergency calls *not* be logged, presumably to
avoid the risk of accidental redialing from the call log UI.
The default is false. -->
<bool name="allow_emergency_numbers_in_call_log">false</bool>
<!-- Determine whether we want to play local DTMF tones in a call, or just let the radio/BP
handle playing of the tones. -->
<bool name="allow_local_dtmf_tones">true</bool>
<!-- Package name for the default in-call UI and dialer [DO NOT TRANSLATE] -->
<string name="ui_default_package" translatable="false">com.google.android.dialer</string>
<!-- Class name for the default in-call UI Service [DO NOT TRANSLATE] -->
<string name="incall_default_class" translatable="false">com.android.incallui.InCallServiceImpl</string>
<!-- Class name for the default main dialer activity [DO NOT TRANSLATE] -->
<string name="dialer_default_class" translatable="false">com.android.dialer.DialtactsActivity</string>
<!-- Flag indicating if the tty is enabled -->
<bool name="tty_enabled">false</bool>