You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Java如何获取双卡手机号?实现类UPI应用短信登录验证

UPI式登录中SIM卡手机号获取问题

我需要实现类似UPI应用的登录功能:用户从手机中选择SIM卡,应用通过该SIM卡发送短信完成手机号验证,后端需对号码进行比对,因此需要获取两张SIM卡的手机号,或用户选中的SIM卡的手机号(可从短信内容或发送环节获取)。

我编写了SimUtil.java工具类处理SIM卡选择和短信操作,尝试通过SubscriptionInfo获取手机号,但该接口能返回运营商名称等信息,唯独手机号返回空字符串。经调研发现,该字段是否填充由运营商决定,无法保证100%能获取到手机号。

以下是我的SimUtil.java代码:

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.telephony.SmsManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class SimUtil {

    List<SimCard> simCards = new ArrayList<>();
    Context context;

    public SimUtil(Context context) {
        this.context = context;
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            setupPermissions();
            return;
        }
        List<SubscriptionInfo> subscriptionInfos = SubscriptionManager.from(context.getApplicationContext()).getActiveSubscriptionInfoList();
        for (int i = 0; i < subscriptionInfos.size(); i++) {
            SubscriptionInfo currentCard = subscriptionInfos.get(i);
            SimCard simCard = new SimCard(i,currentCard);
            simCards.add(simCard);
        }
    }

    public int getSimCardCount() {
        return this.simCards.size();
    }

    public List<SimCard> getSimCardList() {
        if(simCards.size() > 0)
            return simCards;
        else
            return null;
    }

    public SimCard getFirstSimCard() {
        return simCards.get(0);
    }

    public boolean hasDualSim() {
        return simCards.size() > 1;
    }

    public SimCard getSecondSimCard() {
        if(hasDualSim())
            return simCards.get(1);
        else
            return null;
    }

    public static void sendSMSFromSim1(String message) {
        SmsManager.getSmsManagerForSubscriptionId(0)
                .sendTextMessage(AppConstant.LOGIN_MOBILE_NUMBER, null, message, null, null);
    }

    public static void sendSMSFromSim2(String message) {
        SmsManager.getSmsManagerForSubscriptionId(1)
                .sendTextMessage(AppConstant.LOGIN_MOBILE_NUMBER, null, message, null, null);
    }

    private void setupPermissions() {
        int permission = ContextCompat.checkSelfPermission(context, Manifest.permission.SEND_SMS);
        if(permission != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions((Activity)context, new String[]{Manifest.permission.SEND_SMS}, 1);
        }

        int permission2 = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE);
        if(permission2 != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions((Activity)context, new String[]{Manifest.permission.READ_PHONE_STATE}, 1);
        }
    }

    public static class SimCard {
        int index;
        String operatorName;
        String mobileNumber;
        String countryISO;

        public SimCard(int index, String operatorName, String countryISO, String mobileNumber) {
            this.index = index;
            this.operatorName = operatorName;
            this.countryISO = countryISO;
            this.mobileNumber = mobileNumber;
        }

        public SimCard(int index,SubscriptionInfo subscriptionInfo) {
            this.index = index;
            this.operatorName = subscriptionInfo.getCarrierName().toString();
            this.countryISO = subscriptionInfo.getCountryIso();
            this.mobileNumber = subscriptionInfo.getNumber();
        }

        public int getIndex() {
            return index;
        }

        public String getOperatorName() {
            return operatorName;
        }

        public String getCountryISO() {
            return countryISO;
        }

        public String getMobileNumber() {
            return mobileNumber;
        }
    }
}

解决方案

1. 短信发送反向校验(推荐)

既然SubscriptionInfo无法可靠获取手机号,可通过后端接收短信时提取发送方号码实现验证:

  • 在发送的验证短信中加入唯一标识(如设备UUID、临时会话ID),后端收到短信后,提取短信的发送方手机号,再通过标识关联到当前用户会话,验证通过后将结果返回给APP。
  • 无需提前获取手机号,完全依赖短信发送环节的真实号码,兼容性100%。

2. 兜底:用户手动输入+验证

当自动获取失败时,引导用户手动输入所选SIM卡的手机号,再通过该SIM卡发送验证码:

  • 后端比对用户输入的手机号与短信发送方号码,一致则验证通过,确保号码与所选SIM卡匹配。

3. 优化SIM卡信息获取逻辑

补充其他手机号获取方式,提升自动获取的成功率:

// 修改SimCard构造方法,增加TelephonyManager获取逻辑
import android.telephony.TelephonyManager;
import android.text.TextUtils;

public SimCard(int index, SubscriptionInfo subscriptionInfo, Context context) {
    this.index = index;
    this.operatorName = subscriptionInfo.getCarrierName().toString();
    this.countryISO = subscriptionInfo.getCountryIso();
    this.mobileNumber = subscriptionInfo.getNumber();

    // 兜底:用TelephonyManager尝试获取
    if (TextUtils.isEmpty(this.mobileNumber) && ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
        TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        this.mobileNumber = telephonyManager.getLine1Number(subscriptionInfo.getSubscriptionId());
    }
}

注意:getLine1Number()同样依赖运营商配置,部分设备仍可能返回空值。

4. 权限补充

若要监听运营商回执短信,需额外申请RECEIVE_SMS权限,并注册广播接收器解析短信内容。


内容的提问来源于stack exchange,提问作者Shubham Vyas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 00:35:19