Android 11系统下获取SIM卡ICC ID失败的技术咨询
Hey there! That empty string from getIccId() on Android 11 is a common gotcha due to tightened privacy restrictions. Let's break down how to fix this properly:
1. Understand the Permission Change
Starting with Android 11 (API level 30), accessing the ICC ID requires the READ_PHONE_STATE dangerous permission. Your old code likely worked on older versions because permission rules were more lenient, but on Android 11+, you must explicitly request this permission from the user.
2. Step-by-Step Implementation
a. Declare the Permission in AndroidManifest.xml
First, add the permission to your manifest:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
b. Request the Permission Dynamically
Since READ_PHONE_STATE is a dangerous permission, you need to ask the user for it at runtime. Here's how to do that with modern Activity Result APIs:
private static final int REQUEST_READ_PHONE_STATE = 101; // Check if we already have the permission if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) { // Request permission from the user ActivityResultLauncher<String> requestPermissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { if (isGranted) { // Permission approved, proceed to fetch ICC ID fetchIccId(); } else { // Handle permission denial (show a message, disable feature, etc.) Toast.makeText(this, "Permission required to access SIM details", Toast.LENGTH_SHORT).show(); } }); requestPermissionLauncher.launch(Manifest.permission.READ_PHONE_STATE); } else { // Permission already granted, fetch ICC ID directly fetchIccId(); }
c. Fetch the ICC ID Safely
Implement the fetchIccId() method with proper null checks and fallback options:
private void fetchIccId() { SubscriptionManager sm = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); // Handle case where SubscriptionManager isn't available if (sm == null) { Log.e("SIM", "SubscriptionManager service is unavailable"); return; } List<SubscriptionInfo> activeSubscriptions = sm.getActiveSubscriptionInfoList(); // Handle case where no active SIM cards are detected if (activeSubscriptions == null || activeSubscriptions.isEmpty()) { Log.e("SIM", "No active SIM subscriptions found"); return; } // Get the first active subscription (adjust index if you need a specific SIM) SubscriptionInfo subscription = activeSubscriptions.get(0); String iccId = subscription.getIccId(); // Fallback to TelephonyManager if SubscriptionManager returns empty if (TextUtils.isEmpty(iccId)) { TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); if (tm != null) { iccId = tm.getIccId(); // Also requires READ_PHONE_STATE permission } } // Use the retrieved ICC ID if (!TextUtils.isEmpty(iccId)) { Log.d("SIM", "Successfully retrieved ICC ID: " + iccId); } else { Log.e("SIM", "Failed to retrieve ICC ID even with proper permissions"); } }
3. Key Notes
- Device & Profile Restrictions: Some manufacturers or work-managed devices might block ICC ID access even with permission. Test on multiple devices to verify behavior.
- Multi-SIM Support: If your app needs to handle multiple SIM cards, iterate through the
activeSubscriptionslist instead of just taking the first item. - Android 12+ Compatibility: The permission and retrieval logic remains consistent for Android 12 and later, so this code will work across newer versions too.
内容的提问来源于stack exchange,提问作者AlvaroB

