通过拨号盘,输入暗码启动工程模式流程分析。
用工具抓到点击弹出拨号键盘的button的id是fab,点击之后启动DialpadFragment显示拨号盘
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();}});...}
private void showDialpad(boolean animate, boolean fromNewIntent) {...// Show Dialpadif (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();}
/** Fragment that displays a twelve-key phone dialpad. */
public class DialpadFragment extends Fragmentimplements View.OnClickListener,View.OnLongClickListener,View.OnKeyListener,AdapterView.OnItemClickListener,TextWatcher,PopupMenu.OnMenuItemClickListener,DialpadKeyButton.OnPressedListener {
其继承了TextWatcher类,能够监听实现输入变化。
@Overridepublic 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 digitsdigits.getText().clear();}
输入结束后,调用了SpecialCharSequenceMgr辅助类的handleChars方法,这里会对各种特殊的code进行区分处理
public static boolean handleChars(Context context, String input, EditText textField) {// get rid of the separators so that the string gets parsed correctlyString 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 *#*##*#* or "*##"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方法
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中。
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
@Overridepublic 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);}}
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中,最终还是发送暗码广播。
接收示例:
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#*#* mtklogprivate final Uri mMtklogUri1 = Uri.parse("android_secret_code://9527686");@Overridepublic 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);}}}
}