Android 11 旧版本兼容修改:TelephonyManager的getDeviceId和getSubscriberId; WifiManager.setWifiEnabled

概述

    随着Android的更新,越新的版本收紧的权限越来越多,伴随着很多曾经可用的接口慢慢地出现了问题。

比如: TelephonyManager.getSubscriberId()

public static void testGetSubId(Context ctx){
        //Android 11:
        // java.lang.SecurityException: getSubscriberId: The user 10125 does not meet the requirements to access device identifiers.
        TelephonyManager telMgr = (TelephonyManager) ctx.getSystemService(TELEPHONY_SERVICE);
        @SuppressLint("MissingPermission")
        String subId = telMgr.getSubscriberId();
        android.util.Log.d(TAG, "testGetSubId subId=" + subId);
    }

原因

在代码注释中已经写得很明白:
The user 10125 does not meet the requirements to access device identifiers

要做的事情很简单, 找到权限检查的地方, 去掉它
权限检查的位置

frameworks/base/telephony/common/com/android/internal/telephony/TelephonyPermissions.java

    public static boolean checkCallingOrSelfReadDeviceIdentifiers(Context context, int subId,
            String callingPackage, @Nullable String callingFeatureId, String message) {
        return true || checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
                context, subId, callingPackage, callingFeatureId, message, true);
    }
    public static boolean checkCallingOrSelfReadSubscriberIdentifiers(Context context, int subId,
            String callingPackage, @Nullable String callingFeatureId, String message) {
        return true || checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
                context, subId, callingPackage, callingFeatureId, message, false);
    }
    private static boolean checkPrivilegedReadPermissionOrCarrierPrivilegePermission(
            Context context, int subId, String callingPackage, @Nullable String callingFeatureId,
            String message, boolean allowCarrierPrivilegeOnAnySub) {
        int uid = Binder.getCallingUid();
        int pid = Binder.getCallingPid();

        // If the calling package has carrier privileges for specified sub, then allow access.
        if (checkCarrierPrivilegeForSubId(context, subId)) return true;

        // If the calling package has carrier privileges for any subscription
        // and allowCarrierPrivilegeOnAnySub is set true, then allow access.
        if (allowCarrierPrivilegeOnAnySub && checkCarrierPrivilegeForAnySubId(context, uid)) {
            return true;
        }

        PermissionManager permissionManager = (PermissionManager) context.getSystemService(
                Context.PERMISSION_SERVICE);
        if (permissionManager.checkDeviceIdentifierAccess(callingPackage, message, callingFeatureId,
                pid, uid) == PackageManager.PERMISSION_GRANTED) {
            return true;
        }

        return reportAccessDeniedToReadIdentifiers(context, subId, pid, uid, callingPackage,
                message);
    }
    /**
     * Reports a failure when the app with the given pid/uid cannot access the requested identifier.
     *
     * @returns false if the caller is targeting pre-Q and does have the READ_PHONE_STATE
     * permission or carrier privileges.
     * @throws SecurityException if the caller does not meet any of the requirements for the
     *                           requested identifier and is targeting Q or is targeting pre-Q
     *                           and does not have the READ_PHONE_STATE permission or carrier
     *                           privileges.
     */
    private static boolean reportAccessDeniedToReadIdentifiers(Context context, int subId, int pid,
            int uid, String callingPackage, String message) {
        ApplicationInfo callingPackageInfo = null;
        try {
            callingPackageInfo = context.getPackageManager().getApplicationInfoAsUser(
                    callingPackage, 0, UserHandle.getUserHandleForUid(uid));
        } catch (PackageManager.NameNotFoundException e) {
            // If the application info for the calling package could not be found then assume the
            // calling app is a non-preinstalled app to detect any issues with the check
            Log.e(LOG_TAG, "Exception caught obtaining package info for package " + callingPackage,
                    e);
        }
        // The current package should only be reported in StatsLog if it has not previously been
        // reported for the currently invoked device identifier method.
        boolean packageReported = sReportedDeviceIDPackages.containsKey(callingPackage);
        if (!packageReported || !sReportedDeviceIDPackages.get(callingPackage).contains(
                message)) {
            Set invokedMethods;
            if (!packageReported) {
                invokedMethods = new HashSet<String>();
                sReportedDeviceIDPackages.put(callingPackage, invokedMethods);
            } else {
                invokedMethods = sReportedDeviceIDPackages.get(callingPackage);
            }
            invokedMethods.add(message);
            TelephonyCommonStatsLog.write(TelephonyCommonStatsLog.DEVICE_IDENTIFIER_ACCESS_DENIED,
                    callingPackage, message, /* isPreinstalled= */ false, false);
        }
        Log.w(LOG_TAG, "reportAccessDeniedToReadIdentifiers:" + callingPackage + ":" + message + ":"
                + subId);
        // if the target SDK is pre-Q then check if the calling package would have previously
        // had access to device identifiers.
        if (callingPackageInfo != null && (
                callingPackageInfo.targetSdkVersion < Build.VERSION_CODES.Q)) {
            if (context.checkPermission(
                    android.Manifest.permission.READ_PHONE_STATE,
                    pid,
                    uid) == PackageManager.PERMISSION_GRANTED) {
                return false;
            }
            if (checkCarrierPrivilegeForSubId(context, subId)) {
                return false;
            }
        }
        throw new SecurityException(message + ": The user " + uid
                + " does not meet the requirements to access device identifiers.");
    }

frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java

    @Override
    public int checkDeviceIdentifierAccess(@Nullable String packageName, @Nullable String message,
            @Nullable String callingFeatureId, int pid, int uid) {
        // If the check is being requested by an app then only allow the app to query its own
        // access status.
        int callingUid = mInjector.getCallingUid();
        int callingPid = mInjector.getCallingPid();
        if (UserHandle.getAppId(callingUid) >= Process.FIRST_APPLICATION_UID && (callingUid != uid
                || callingPid != pid)) {
            String response = String.format(
                    "Calling uid %d, pid %d cannot check device identifier access for package %s "
                            + "(uid=%d, pid=%d)",
                    callingUid, callingPid, packageName, uid, pid);
            Log.w(TAG, response);
            throw new SecurityException(response);
        }
        // Allow system and root access to the device identifiers.
        final int appId = UserHandle.getAppId(uid);
        if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // Allow access to packages that have the READ_PRIVILEGED_PHONE_STATE permission.
        if (mInjector.checkPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, pid,
                uid) == PackageManager.PERMISSION_GRANTED) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // If the calling package is not null then perform the appop and device / profile owner
        // check.
        if (packageName != null) {
            // Allow access to a package that has been granted the READ_DEVICE_IDENTIFIERS appop.
            long token = mInjector.clearCallingIdentity();
            AppOpsManager appOpsManager = (AppOpsManager) mInjector.getSystemService(
                    Context.APP_OPS_SERVICE);
            try {
                if (appOpsManager.noteOpNoThrow(AppOpsManager.OPSTR_READ_DEVICE_IDENTIFIERS, uid,
                        packageName, callingFeatureId, message) == AppOpsManager.MODE_ALLOWED) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            } finally {
                mInjector.restoreCallingIdentity(token);
            }
            // Check if the calling packages meets the device / profile owner requirements for
            // identifier access.
            DevicePolicyManager devicePolicyManager =
                    (DevicePolicyManager) mInjector.getSystemService(Context.DEVICE_POLICY_SERVICE);
            if (devicePolicyManager != null && devicePolicyManager.hasDeviceIdentifierAccess(
                    packageName, pid, uid)) {
                return PackageManager.PERMISSION_GRANTED;
            }
        }
        return PackageManager.PERMISSION_DENIED;
    }


WifiManager.setWifiEnabled

    从官方文档中已经说明: SDK >= Q 这个接口会直接返回 false,不再生效
在这里插入图片描述
示例

build.gradle 注意targetSdkVersion

    defaultConfig {
        applicationId "com.xxx.apitester"
        minSdkVersion 21
        targetSdkVersion 30
		//...
    }
		WifiManager wifiMgr = (WifiManager) ctx.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        boolean en = wifiMgr.isWifiEnabled();
        //返回false
        boolean res = wifiMgr.setWifiEnabled(!en);

如果调用了从LOG中可以找到: setWifiEnabled not allowed for uid=xxx

原因

frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java

    /**
     * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}
     * @param enable {@code true} to enable, {@code false} to disable.
     * @return {@code true} if the enable/disable operation was
     *         started or is already in the queue.
     */
    @Override
    public synchronized boolean setWifiEnabled(String packageName, boolean enable) {
        if (enforceChangePermission(packageName) != MODE_ALLOWED) {
            return false;
        }
/*********************************/
//检查APP的SDK版本等信息。
/*********************************/
        boolean isPrivileged = isPrivileged(Binder.getCallingPid(), Binder.getCallingUid());
        if (!isPrivileged && !isDeviceOrProfileOwner(Binder.getCallingUid(), packageName)
                && !mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q,
                  Binder.getCallingUid())
                && !isSystem(packageName, Binder.getCallingUid())) {
            mLog.info("setWifiEnabled not allowed for uid=%")
                    .c(Binder.getCallingUid()).flush();
            return false;
        }
        // If Airplane mode is enabled, only privileged apps are allowed to toggle Wifi
        if (mSettingsStore.isAirplaneModeOn() && !isPrivileged) {
            mLog.err("setWifiEnabled in Airplane mode: only Settings can toggle wifi").flush();
            return false;
        }

        // If SoftAp is enabled, only privileged apps are allowed to toggle wifi
        if (!isPrivileged && mTetheredSoftApTracker.getState() == WIFI_AP_STATE_ENABLED) {
            mLog.err("setWifiEnabled with SoftAp enabled: only Settings can toggle wifi").flush();
            return false;
        }

        mLog.info("setWifiEnabled package=% uid=% enable=%").c(packageName)
                .c(Binder.getCallingUid()).c(enable).flush();
        long ident = Binder.clearCallingIdentity();
        try {
            if (!mSettingsStore.handleWifiToggled(enable)) {
                // Nothing to do if wifi cannot be toggled
                return true;
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        if (mWifiPermissionsUtil.checkNetworkSettingsPermission(Binder.getCallingUid())) {
            if (enable) {
                mWifiMetrics.logUserActionEvent(UserActionEvent.EVENT_TOGGLE_WIFI_ON);
            } else {
                WifiInfo wifiInfo = mClientModeImpl.syncRequestConnectionInfo();
                mWifiMetrics.logUserActionEvent(UserActionEvent.EVENT_TOGGLE_WIFI_OFF,
                        wifiInfo == null ? -1 : wifiInfo.getNetworkId());
            }
        }
        mWifiMetrics.incrementNumWifiToggles(isPrivileged, enable);
        mActiveModeWarden.wifiToggled();
        return true;
    }

检查版本低于:Build.VERSION_CODES.Q

frameworks/opt/net/wifi/service/java/com/android/server/wifi/util/WifiPermissionsUtil.java


    /**
     * Checks whether than the target SDK of the package is less than the specified version code.
     */
    public boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
    	//强制返回,版本检测将不再生效
    	if(true)return true;
        long ident = Binder.clearCallingIdentity();
        try {
            if (mContext.getPackageManager().getApplicationInfoAsUser(
                    packageName, 0,
                    UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion
                    < versionCode) {
                return true;
            }
        } catch (PackageManager.NameNotFoundException e) {
            // In case of exception, assume unknown app (more strict checking)
            // Note: This case will never happen since checkPackage is
            // called to verify validity before checking App's version.
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return false;
    }

参考

  • 第三方APP无法申请READ_PRIVILEGED_PHONE_STATE权限:
Android 10 changes the permissions for device identifiers so that all device identifiers are now protected by the READ_PRIVILEGED_PHONE_STATE permission. 
Prior to Android 10, persistent device identifiers (IMEI/MEID, IMSI, SIM, and build serial) were protected behind the READ_PHONE_STATE runtime permission. 
The READ_PRIVILEGED_PHONE_STATE permission is only granted to apps signed with the platform key and privileged system apps.

How to request for permission: READ_PRIVILEGED_PHONE_STATE?

Android 获取双卡手机IMEI,IMSI,ICCID

<think>好的,我现在需要分析用户提供的日志信息:“OplusUIFirst handleProcessStart com.android.carrierconfig 日志解析 10209 7170 过程启动 调试信息”。首先,我需要拆解各个部分的含义。 首先,“OplusUIFirst”可能指的是Oppo或一加手机的UI框架,因为“Oplus”是他们的品牌标识。handleProcessStart可能是一个处理进程启动的方法或模块。接下来是“com.android.carrierconfig”,这是Android系统中用于运营商配置的包名,负责加载运营商特定的配置信息。 PID 10209UID 7170分别代表进程ID用户ID,说明这个进程是由系统或某个应用启动的。日志中的“过程启动”“调试信息”表明这是一个进程启动时的调试日志,可能用于跟踪系统行为或排查问题。 接下来,用户想知道这个日志的具体含义以及com.android.carrierconfig的相关信息。我需要确认carrierconfig的作用,它通常处理SIM卡运营商的配置,比如APN设置、网络参数等。当手机检测到SIM卡时,系统可能会启动这个服务来加载相应的配置。 关于OplusUIFirst的handleProcessStart,可能涉及到Oppo自定义的Android系统层对进程启动的监控或管理,比如资源分配、权限检查或性能优化。需要查阅相关文档或源码来确认其具体功能,但根据常见做法,这类模块可能在进程创建时执行特定的初始化或策略应用。 用户可能遇到的场景包括SIM卡配置问题、运营商相关功能异常,或者系统日志中出现相关错误。调试建议包括检查SIM卡状态、运营商配置是否正常,或者查看是否有其他相关错误日志。 最后,生成相关问题需要考虑用户可能的后续疑问,比如日志的详细解析方法、配置问题排查步骤,或者Oppo系统特有的进程管理机制。</think>### OplusUIFirst handleProcessStart 日志解析 #### 1. 日志结构分析 日志条目 `OplusUIFirst handleProcessStart com.android.carrierconfig 10209 7170 过程启动 调试信息` 包含以下关键信息: - **OplusUIFirst**:Oppo/一加设备系统层自定义框架模块,用于管理进程生命周期、资源调度等系统级行为[^1] - **handleProcessStart**:进程启动事件触发的处理函数,可能涉及权限检查、资源预分配或性能监控 - **com.android.carrierconfig**:Android系统预置的运营商配置服务,包路径为 `/system/priv-app/CarrierConfig` - **1029/7170**:PID(进程ID) UID(用户ID),其中 UID 7170 属于系统级用户 - **过程启动**:表示进程启动事件类型 #### 2. CarrierConfig 服务功能 该服务主要职责包括: ``` $$ \text{功能} = \begin{cases} 1. \text{加载运营商定制配置(APN/MMS/VoLTE等)} \\ 2. \text{解析carrier\_config\_*.xml配置文件} \\ 3. \text{提供TelephonyManager.getCarrierConfig() API} \end{cases} $$ ``` 配置文件存储路径:`/vendor/rfs/msm/mpss/readonly/vendor/mbn/mcfg_sw/generic/` #### 3. 典型调试场景 当出现以下问题时需要关注此类日志: 1. SIM卡识别异常 2. 网络注册失败(4G/5G) 3. 运营商特色功能未生效(如VolTE开关) 4. 双卡设备配置冲突 #### 4. 日志分析建议步骤 ```bash # 1. 过滤相关进程日志 adb logcat | grep -E 'OplusUIFirst|CarrierConfig' # 2. 检查配置文件状态 adb shell ls -l /vendor/mbn/mcfg_sw/generic/ # 3. 获取当前配置快照 adb shell dumpsys carrier_config ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值