Andorid11 暗码启动应用(一)

本文详细分析了Android拨号盘中输入特定暗码启动工程模式的过程,从点击拨号盘按钮,到输入暗码后的逻辑处理,再到调用TelephonyManager处理秘密代码,最后广播触发接收器启动相关应用。主要涉及DialpadFragment、SpecialCharSequenceMgr和TelephonyManagerCompat等组件,揭示了系统如何识别并响应特殊代码。
摘要由CSDN通过智能技术生成

通过拨号盘,输入暗码启动工程模式流程分析。

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);
			}
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值