• Andorid11 暗码启动应用(一)


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

    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();
              }
            });
        ...
     }   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 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
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    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 {
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    其继承了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();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输入结束后,调用了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 *#*##*#* 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;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    然后又来到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);
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    这里会判断版本和是否是默认的通话应用,如果是低于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();
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 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());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    走到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>        
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 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);
    			}
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
  • 相关阅读:
    栈和队列 OJ题
    MQTT over QUIC:下一代物联网标准协议为消息传输场景注入新动力
    旅游票务商城小程序的作用是什么
    Java中所有关键字简介说明
    MySQL的行锁和表锁
    我哪些年未发布的文字
    测试左移和测试右移,我们为何要“上下求索”?
    kotlin 类
    Java的反射应用(Method和Class)
    php的加密方式汇总
  • 原文地址:https://blog.csdn.net/wxd_csdn_2016/article/details/127807978