通过拨号盘,输入暗码启动工程模式流程分析。
1.暗码启动应用流程
1.1拨号盘的显示
用工具抓到点击弹出拨号键盘的button的id是fab,点击之后启动DialpadFragment显示拨号盘
- packages/apps/Dialer/java/com/android/dialer/main/impl/OldMainActivityPeer.java
private void initLayout(Bundle savedInstanceState) {
...
FloatingActionButton fab = activity.findViewById(R.id.fab);
fab.setOnClickListener(
v -> {
Logger.get(activity).logImpression(DialerImpression.Type.MAIN_CLICK_FAB_TO_OPEN_DIALPAD);
searchController.showDialpad(true);
if (callLogAdapterOnActionModeStateChangedListener.isEnabled) {
LogUtil.i("OldMainActivityPeer.onFabClicked", "closing multiselect");
callLogAdapterOnActionModeStateChangedListener.actionMode.finish();
}
});
...
}
- packages/apps/Dialer/java/com/android/dialer/main/impl/MainSearchController.java
private void showDialpad(boolean animate, boolean fromNewIntent) {
...
// Show Dialpad
if (dialpadFragment == null) {
dialpadFragment = new DialpadFragment();
dialpadFragment.setStartedFromNewIntent(fromNewIntent);
transaction.add(R.id.dialpad_fragment_container, dialpadFragment, DIALPAD_FRAGMENT_TAG);
searchFragment.setQuery("", CallInitiationType.Type.DIALPAD);
} else {
dialpadFragment.setStartedFromNewIntent(fromNewIntent);
transaction.show(dialpadFragment);
}
transaction.commit();
}
1.2 输入暗码后的逻辑处理
- packages/apps/Dialer/java/com/android/dialer/dialpadview/DialpadFragment.java
/** Fragment that displays a twelve-key phone dialpad. */
public class DialpadFragment extends Fragment
implements View.OnClickListener,
View.OnLongClickListener,
View.OnKeyListener,
AdapterView.OnItemClickListener,
TextWatcher,
PopupMenu.OnMenuItemClickListener,
DialpadKeyButton.OnPressedListener {
其继承了TextWatcher类,能够监听实现输入变化。
@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();
}
输入结束后,调用了SpecialCharSequenceMgr辅助类的handleChars方法,这里会对各种特殊的code进行区分处理
- packages/apps/Dialer/java/com/android/dialer/dialpadview/SpecialCharSequenceMgr.java
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;
}
// 以*#*#开头,#*#*结束的特殊code,发广播调起其他应用
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方法
- packages/apps/Dialer/java/com/android/dialer/compat/telephony/TelephonyManagerCompat.java
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);
}
}
这里会判断版本和是否是默认的通话应用,如果是低于O版本直接发送广播,如果是高于O版本,判断是否是默认通话应用,是的话走到TelephonyManager中。
- frameworks/base/telephony/java/android/telephony/TelephonyManager.java
public void sendDialerSpecialCode(String inputCode) {
try {
final ITelephony telephony = getITelephony();
if (telephony == null) {
if (!isSystemProcess()) {
throw new RuntimeException("Telephony service unavailable");
}
return;
}
telephony.sendDialerSpecialCode(mContext.getOpPackageName(), inputCode);
} catch (RemoteException ex) {
// This could happen if binder process crashes.
if (!isSystemProcess()) {
ex.rethrowAsRuntimeException();
}
}
}
PhoneInterfaceManager继承ITelephony.Stub
- packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java
@Override
public void sendDialerSpecialCode(String callingPackage, String inputCode) {
final Phone defaultPhone = getDefaultPhone();
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
TelecomManager tm = defaultPhone.getContext().getSystemService(TelecomManager.class);
String defaultDialer = tm.getDefaultDialerPackage();
if (!TextUtils.equals(callingPackage, defaultDialer)) {
TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mApp,
getDefaultSubscription(), "sendDialerSpecialCode");
}
final long identity = Binder.clearCallingIdentity();
try {
defaultPhone.sendDialerSpecialCode(inputCode);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- frameworks/opt/telephony/src/java/com/android/internal/telephony/Phone.java
public void sendDialerSpecialCode(String code) {
if (!TextUtils.isEmpty(code)) {
final BroadcastOptions options = BroadcastOptions.makeBasic();
options.setBackgroundActivityStartsAllowed(true);
Intent intent = new Intent(TelephonyIntents.SECRET_CODE_ACTION,
Uri.parse("android_secret_code://" + code));
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcast(intent, null, options.toBundle());
// {@link TelephonyManager.ACTION_SECRET_CODE} will replace {@link
// TelephonyIntents#SECRET_CODE_ACTION} in the next Android version. Before
// that both of these two actions will be broadcast.
Intent secrectCodeIntent = new Intent(TelephonyManager.ACTION_SECRET_CODE,
Uri.parse("android_secret_code://" + code));
secrectCodeIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcast(secrectCodeIntent, null, options.toBundle());
}
}
走到Phone.java中,最终还是发送暗码广播。
接收示例:
- vendor/mediatek/proprietary/packages/apps/EngineerMode/AndroidManifest.xml
<receiver
android:name=".EngineerModeReceiver"
android:exported="true" >
<intent-filter>
<action android:name="android.provider.Telephony.SECRET_CODE" />
<data
android:host="3646633"
android:scheme="android_secret_code" />
</intent-filter>
<intent-filter>
<action android:name="android.provider.Telephony.SECRET_CODE" />
<data
android:host="9527686"
android:scheme="android_secret_code" />
</intent-filter>
</receiver>
- vendor/mediatek/proprietary/packages/apps/EngineerMode/src/com/mediatek/engineermode/EngineerModeReceiver.java
public final class EngineerModeReceiver extends BroadcastReceiver {
private static final String TAG = "EngineerModeReceiver";
private static final String SECRET_CODE_ACTION
= "android.provider.Telephony.SECRET_CODE";
// process *#*#3646633#*#*
private final Uri mEmUri = Uri.parse("android_secret_code://3646633");
// process *#*#9527686#*#* mtklog
private final Uri mMtklogUri1 = Uri.parse("android_secret_code://9527686");
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == null) {
Elog.e(TAG, "Null action");
return;
}
if (intent.getAction().equals(SECRET_CODE_ACTION)) {
Uri uri = intent.getData();
Elog.i(TAG, "Receive secret code intent and uri is " + uri);
if (uri.equals(mEmUri)) {
Intent intentEm = new Intent(context, EngineerMode.class);
intentEm.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intentEm);
} else if(uri.equals(mMtklogUri1)) {
String packageName = "com.debug.loggerui";
String className = "com.debug.loggerui.MainActivity";
Intent intentEm = new Intent(Intent.ACTION_MAIN);
intentEm.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ComponentName cn = new ComponentName(packageName, className);
intentEm.setComponent(cn);
context.startActivity(intentEm);
}
}
}
}