| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. |
| * Not a Contribution. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony; |
| |
| import static android.Manifest.permission.MODIFY_PHONE_STATE; |
| import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; |
| import static android.telephony.TelephonyManager.ENABLE_FEATURE_MAPPING; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.AppOpsManager; |
| import android.app.compat.CompatChanges; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.content.Context; |
| import android.content.pm.PackageManager; |
| import android.net.Uri; |
| import android.os.Binder; |
| import android.os.Build; |
| import android.os.RemoteException; |
| import android.os.SystemProperties; |
| import android.os.TelephonyServiceManager.ServiceRegisterer; |
| import android.telephony.ImsiEncryptionInfo; |
| import android.telephony.PhoneNumberUtils; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyFrameworkInitializer; |
| import android.text.TextUtils; |
| import android.util.EventLog; |
| |
| import com.android.internal.telephony.flags.FeatureFlags; |
| import com.android.internal.telephony.flags.FeatureFlagsImpl; |
| import com.android.internal.telephony.subscription.SubscriptionInfoInternal; |
| import com.android.internal.telephony.subscription.SubscriptionManagerService; |
| import com.android.internal.telephony.uicc.IsimRecords; |
| import com.android.internal.telephony.uicc.SIMRecords; |
| import com.android.internal.telephony.uicc.UiccCardApplication; |
| import com.android.internal.telephony.uicc.UiccPort; |
| import com.android.telephony.Rlog; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| public class PhoneSubInfoController extends IPhoneSubInfo.Stub { |
| private static final String TAG = "PhoneSubInfoController"; |
| private static final boolean DBG = true; |
| private static final boolean VDBG = false; // STOPSHIP if true |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private final Context mContext; |
| private AppOpsManager mAppOps; |
| private FeatureFlags mFeatureFlags; |
| private PackageManager mPackageManager; |
| private final int mVendorApiLevel; |
| |
| public PhoneSubInfoController(Context context) { |
| this(context, new FeatureFlagsImpl()); |
| } |
| |
| public PhoneSubInfoController(Context context, FeatureFlags featureFlags) { |
| ServiceRegisterer phoneSubServiceRegisterer = TelephonyFrameworkInitializer |
| .getTelephonyServiceManager() |
| .getPhoneSubServiceRegisterer(); |
| if (phoneSubServiceRegisterer.get() == null) { |
| phoneSubServiceRegisterer.register(this); |
| } |
| mAppOps = context.getSystemService(AppOpsManager.class); |
| mContext = context; |
| mPackageManager = context.getPackageManager(); |
| mFeatureFlags = featureFlags; |
| mVendorApiLevel = SystemProperties.getInt( |
| "ro.vendor.api_level", Build.VERSION.DEVICE_INITIAL_SDK_INT); |
| } |
| |
| @Deprecated |
| public String getDeviceId(String callingPackage) { |
| return getDeviceIdWithFeature(callingPackage, null); |
| } |
| |
| public String getDeviceIdWithFeature(String callingPackage, String callingFeatureId) { |
| return getDeviceIdForPhone(SubscriptionManager.getPhoneId(getDefaultSubscription()), |
| callingPackage, callingFeatureId); |
| } |
| |
| public String getDeviceIdForPhone(int phoneId, String callingPackage, |
| String callingFeatureId) { |
| enforceCallingPackageUidMatched(callingPackage); |
| return callPhoneMethodForPhoneIdWithReadDeviceIdentifiersCheck(phoneId, callingPackage, |
| callingFeatureId, "getDeviceId", (phone) -> phone.getDeviceId()); |
| } |
| |
| public String getNaiForSubscriber(int subId, String callingPackage, String callingFeatureId) { |
| return callPhoneMethodForSubIdWithReadSubscriberIdentifiersCheck(subId, callingPackage, |
| callingFeatureId, "getNai", (phone)-> { |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, |
| "getNaiForSubscriber"); |
| |
| return phone.getNai(); |
| }); |
| } |
| |
| public String getImeiForSubscriber(int subId, String callingPackage, |
| String callingFeatureId) { |
| return callPhoneMethodForSubIdWithReadDeviceIdentifiersCheck(subId, callingPackage, |
| callingFeatureId, "getImei", (phone) -> phone.getImei()); |
| } |
| |
| public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int subId, int keyType, |
| String callingPackage) { |
| return callPhoneMethodForSubIdWithPrivilegedCheck(subId, |
| "getCarrierInfoForImsiEncryption", |
| (phone)-> { |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, |
| "getCarrierInfoForImsiEncryption"); |
| |
| return phone.getCarrierInfoForImsiEncryption(keyType, true); |
| }); |
| } |
| |
| public void setCarrierInfoForImsiEncryption(int subId, String callingPackage, |
| ImsiEncryptionInfo imsiEncryptionInfo) { |
| callPhoneMethodForSubIdWithModifyCheck(subId, callingPackage, |
| "setCarrierInfoForImsiEncryption", |
| (phone)-> { |
| phone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo); |
| return null; |
| }); |
| } |
| |
| /** |
| * Resets the Carrier Keys in the database. This involves 2 steps: |
| * 1. Delete the keys from the database. |
| * 2. Send an intent to download new Certificates. |
| * @param subId |
| * @param callingPackage |
| */ |
| public void resetCarrierKeysForImsiEncryption(int subId, String callingPackage) { |
| callPhoneMethodForSubIdWithModifyCheck(subId, callingPackage, |
| "resetCarrierKeysForImsiEncryption", |
| (phone)-> { |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, |
| "resetCarrierKeysForImsiEncryption"); |
| |
| phone.resetCarrierKeysForImsiEncryption(); |
| return null; |
| }); |
| } |
| |
| public String getDeviceSvn(String callingPackage, String callingFeatureId) { |
| return getDeviceSvnUsingSubId(getDefaultSubscription(), callingPackage, callingFeatureId); |
| } |
| |
| public String getDeviceSvnUsingSubId(int subId, String callingPackage, |
| String callingFeatureId) { |
| return callPhoneMethodForSubIdWithReadCheck(subId, callingPackage, callingFeatureId, |
| "getDeviceSvn", (phone)-> phone.getDeviceSvn()); |
| } |
| |
| @Deprecated |
| public String getSubscriberId(String callingPackage) { |
| return getSubscriberIdWithFeature(callingPackage, null); |
| } |
| |
| public String getSubscriberIdWithFeature(String callingPackage, String callingFeatureId) { |
| return getSubscriberIdForSubscriber(getDefaultSubscription(), callingPackage, |
| callingFeatureId); |
| } |
| |
| public String getSubscriberIdForSubscriber(int subId, String callingPackage, |
| String callingFeatureId) { |
| String message = "getSubscriberIdForSubscriber"; |
| mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); |
| |
| long identity = Binder.clearCallingIdentity(); |
| boolean isActive; |
| try { |
| isActive = SubscriptionManagerService.getInstance().isActiveSubId(subId, |
| callingPackage, callingFeatureId); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| if (isActive) { |
| return callPhoneMethodForSubIdWithReadSubscriberIdentifiersCheck(subId, callingPackage, |
| callingFeatureId, message, (phone) -> { |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, |
| "getSubscriberIdForSubscriber"); |
| |
| return phone.getSubscriberId(); |
| }); |
| } else { |
| if (!TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers( |
| mContext, subId, callingPackage, callingFeatureId, message)) { |
| return null; |
| } |
| |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSubscriberIdForSubscriber"); |
| |
| identity = Binder.clearCallingIdentity(); |
| try { |
| SubscriptionInfoInternal subInfo = SubscriptionManagerService.getInstance() |
| .getSubscriptionInfoInternal(subId); |
| if (subInfo != null && !TextUtils.isEmpty(subInfo.getImsi())) { |
| return subInfo.getImsi(); |
| } |
| return null; |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| } |
| |
| @Deprecated |
| public String getIccSerialNumber(String callingPackage) { |
| return getIccSerialNumberWithFeature(callingPackage, null); |
| } |
| |
| /** |
| * Retrieves the serial number of the ICC, if applicable. |
| */ |
| public String getIccSerialNumberWithFeature(String callingPackage, String callingFeatureId) { |
| return getIccSerialNumberForSubscriber(getDefaultSubscription(), callingPackage, |
| callingFeatureId); |
| } |
| |
| public String getIccSerialNumberForSubscriber(int subId, String callingPackage, |
| String callingFeatureId) { |
| return callPhoneMethodForSubIdWithReadSubscriberIdentifiersCheck(subId, callingPackage, |
| callingFeatureId, "getIccSerialNumber", (phone) -> { |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, |
| "getIccSerialNumberForSubscriber"); |
| |
| return phone.getIccSerialNumber(); |
| }); |
| } |
| |
| public String getLine1Number(String callingPackage, String callingFeatureId) { |
| return getLine1NumberForSubscriber(getDefaultSubscription(), callingPackage, |
| callingFeatureId); |
| } |
| |
| // In R and beyond, READ_PHONE_NUMBERS includes READ_PHONE_NUMBERS and READ_SMS only. |
| // Prior to R, it also included READ_PHONE_STATE. Maintain that for compatibility. |
| public String getLine1NumberForSubscriber(int subId, String callingPackage, |
| String callingFeatureId) { |
| return callPhoneMethodForSubIdWithReadPhoneNumberCheck( |
| subId, callingPackage, callingFeatureId, "getLine1Number", |
| (phone)-> { |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, |
| "getLine1NumberForSubscriber"); |
| |
| return phone.getLine1Number(); |
| }); |
| } |
| |
| public String getLine1AlphaTag(String callingPackage, String callingFeatureId) { |
| return getLine1AlphaTagForSubscriber(getDefaultSubscription(), callingPackage, |
| callingFeatureId); |
| } |
| |
| public String getLine1AlphaTagForSubscriber(int subId, String callingPackage, |
| String callingFeatureId) { |
| return callPhoneMethodForSubIdWithReadCheck(subId, callingPackage, callingFeatureId, |
| "getLine1AlphaTag", (phone)-> phone.getLine1AlphaTag()); |
| } |
| |
| public String getMsisdn(String callingPackage, String callingFeatureId) { |
| return getMsisdnForSubscriber(getDefaultSubscription(), callingPackage, callingFeatureId); |
| } |
| |
| // In R and beyond this will require READ_PHONE_NUMBERS. |
| // Prior to R it needed READ_PHONE_STATE. Maintain that for compatibility. |
| public String getMsisdnForSubscriber(int subId, String callingPackage, |
| String callingFeatureId) { |
| return callPhoneMethodForSubIdWithReadPhoneNumberCheck( |
| subId, callingPackage, callingFeatureId, "getMsisdn", (phone)-> phone.getMsisdn()); |
| } |
| |
| public String getVoiceMailNumber(String callingPackage, String callingFeatureId) { |
| return getVoiceMailNumberForSubscriber(getDefaultSubscription(), callingPackage, |
| callingFeatureId); |
| } |
| |
| public String getVoiceMailNumberForSubscriber(int subId, String callingPackage, |
| String callingFeatureId) { |
| return callPhoneMethodForSubIdWithReadCheck(subId, callingPackage, callingFeatureId, |
| "getVoiceMailNumber", (phone)-> { |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_CALLING, |
| "getVoiceMailNumberForSubscriber"); |
| |
| String number = PhoneNumberUtils.extractNetworkPortion( |
| phone.getVoiceMailNumber()); |
| if (VDBG) log("VM: getVoiceMailNUmber: " + number); |
| return number; |
| }); |
| } |
| |
| public String getVoiceMailAlphaTag(String callingPackage, String callingFeatureId) { |
| return getVoiceMailAlphaTagForSubscriber(getDefaultSubscription(), callingPackage, |
| callingFeatureId); |
| } |
| |
| public String getVoiceMailAlphaTagForSubscriber(int subId, String callingPackage, |
| String callingFeatureId) { |
| return callPhoneMethodForSubIdWithReadCheck(subId, callingPackage, callingFeatureId, |
| "getVoiceMailAlphaTag", (phone)-> { |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_CALLING, |
| "getVoiceMailAlphaTagForSubscriber"); |
| |
| return phone.getVoiceMailAlphaTag(); |
| }); |
| } |
| |
| /** |
| * get Phone object based on subId. |
| **/ |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private Phone getPhone(int subId) { |
| int phoneId = SubscriptionManager.getPhoneId(subId); |
| if (!SubscriptionManager.isValidPhoneId(phoneId)) { |
| return null; |
| } |
| return PhoneFactory.getPhone(phoneId); |
| } |
| |
| private void enforceCallingPackageUidMatched(String callingPackage) { |
| try { |
| mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); |
| } catch (SecurityException se) { |
| EventLog.writeEvent(0x534e4554, "188677422", Binder.getCallingUid()); |
| throw se; |
| } |
| } |
| |
| private boolean enforceIccSimChallengeResponsePermission(Context context, int subId, |
| String callingPackage, String callingFeatureId, String message) { |
| if (TelephonyPermissions.checkCallingOrSelfUseIccAuthWithDeviceIdentifier(context, |
| callingPackage, callingFeatureId, message)) { |
| return true; |
| } |
| if (VDBG) log("No USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER permission."); |
| enforcePrivilegedPermissionOrCarrierPrivilege(subId, message); |
| return true; |
| } |
| |
| /** |
| * Make sure caller has either read privileged phone permission or carrier privilege. |
| * |
| * @throws SecurityException if the caller does not have the required permission/privilege |
| */ |
| private void enforcePrivilegedPermissionOrCarrierPrivilege(int subId, String message) { |
| // TODO(b/73660190): Migrate to |
| // TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivileges and delete |
| // this helper method. |
| int permissionResult = mContext.checkCallingOrSelfPermission( |
| READ_PRIVILEGED_PHONE_STATE); |
| if (permissionResult == PackageManager.PERMISSION_GRANTED) { |
| return; |
| } |
| if (VDBG) log("No read privileged phone permission, check carrier privilege next."); |
| TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(mContext, subId, message); |
| } |
| |
| /** |
| * Make sure caller has modify phone state permission. |
| */ |
| private void enforceModifyPermission() { |
| mContext.enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, |
| "Requires MODIFY_PHONE_STATE"); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private int getDefaultSubscription() { |
| return PhoneFactory.getDefaultSubscription(); |
| } |
| |
| /** |
| * get the Isim Impi based on subId |
| */ |
| public String getIsimImpi(int subId) { |
| return callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getIsimImpi", |
| (phone) -> { |
| IsimRecords isim = phone.getIsimRecords(); |
| if (isim != null) { |
| return isim.getIsimImpi(); |
| } else { |
| return null; |
| } |
| }); |
| } |
| |
| /** |
| * Fetches the IMS private user identity (EF_IMPI) based on subscriptionId. |
| * |
| * @param subId subscriptionId |
| * @return IMPI (IMS private user identity) of type string. |
| * @throws IllegalArgumentException if the subscriptionId is not valid |
| * @throws IllegalStateException in case the ISIM hasn’t been loaded. |
| * @throws SecurityException if the caller does not have the required permission |
| */ |
| public String getImsPrivateUserIdentity(int subId, String callingPackage, |
| String callingFeatureId) { |
| if (!SubscriptionManager.isValidSubscriptionId(subId)) { |
| throw new IllegalArgumentException("Invalid SubscriptionID = " + subId); |
| } |
| if (!TelephonyPermissions.checkCallingOrSelfUseIccAuthWithDeviceIdentifier(mContext, |
| callingPackage, callingFeatureId, "getImsPrivateUserIdentity")) { |
| throw (new SecurityException("No permissions to the caller")); |
| } |
| Phone phone = getPhone(subId); |
| assert phone != null; |
| IsimRecords isim = phone.getIsimRecords(); |
| if (isim != null) { |
| return isim.getIsimImpi(); |
| } else { |
| throw new IllegalStateException("ISIM is not loaded"); |
| } |
| } |
| |
| /** |
| * get the Isim Domain based on subId |
| */ |
| public String getIsimDomain(int subId) { |
| return callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getIsimDomain", |
| (phone) -> { |
| enforceTelephonyFeatureWithException(getCurrentPackageName(), |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getIsimDomain"); |
| |
| IsimRecords isim = phone.getIsimRecords(); |
| if (isim != null) { |
| return isim.getIsimDomain(); |
| } else { |
| return null; |
| } |
| }); |
| } |
| |
| /** |
| * get the Isim Impu based on subId |
| */ |
| public String[] getIsimImpu(int subId) { |
| return callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getIsimImpu", |
| (phone) -> { |
| IsimRecords isim = phone.getIsimRecords(); |
| if (isim != null) { |
| return isim.getIsimImpu(); |
| } else { |
| return null; |
| } |
| }); |
| } |
| |
| /** |
| * Fetches the ISIM public user identities (EF_IMPU) from UICC based on subId |
| * |
| * @param subId subscriptionId |
| * @param callingPackage package name of the caller |
| * @param callingFeatureId feature Id of the caller |
| * @return List of public user identities of type android.net.Uri or empty list if |
| * EF_IMPU is not available. |
| * @throws IllegalArgumentException if the subscriptionId is not valid |
| * @throws IllegalStateException in case the ISIM hasn’t been loaded. |
| * @throws SecurityException if the caller does not have the required permission |
| */ |
| public List<Uri> getImsPublicUserIdentities(int subId, String callingPackage, |
| String callingFeatureId) { |
| if (TelephonyPermissions. |
| checkCallingOrSelfReadPrivilegedPhoneStatePermissionOrReadPhoneNumber( |
| mContext, subId, callingPackage, callingFeatureId, "getImsPublicUserIdentities")) { |
| |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getImsPublicUserIdentities"); |
| |
| Phone phone = getPhone(subId); |
| assert phone != null; |
| IsimRecords isimRecords = phone.getIsimRecords(); |
| if (isimRecords != null) { |
| String[] impus = isimRecords.getIsimImpu(); |
| List<Uri> impuList = new ArrayList<>(); |
| for (String impu : impus) { |
| if (impu != null && impu.trim().length() > 0) { |
| impuList.add(Uri.parse(impu)); |
| } |
| } |
| return impuList; |
| } |
| throw new IllegalStateException("ISIM is not loaded"); |
| } else { |
| throw new IllegalArgumentException("Invalid SubscriptionID = " + subId); |
| } |
| } |
| |
| /** |
| * get the Isim Ist based on subId |
| */ |
| public String getIsimIst(int subId) throws RemoteException { |
| return callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getIsimIst", |
| (phone) -> { |
| enforceTelephonyFeatureWithException(getCurrentPackageName(), |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getIsimIst"); |
| |
| IsimRecords isim = phone.getIsimRecords(); |
| if (isim != null) { |
| return isim.getIsimIst(); |
| } else { |
| return null; |
| } |
| }); |
| } |
| |
| /** |
| * get the Isim Pcscf based on subId |
| */ |
| public String[] getIsimPcscf(int subId) throws RemoteException { |
| return callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getIsimPcscf", |
| (phone) -> { |
| IsimRecords isim = phone.getIsimRecords(); |
| if (isim != null) { |
| return isim.getIsimPcscf(); |
| } else { |
| return null; |
| } |
| }); |
| } |
| |
| /** |
| * Returns the USIM service table that fetched from EFUST elementary field that are loaded |
| * based on the appType. |
| */ |
| public String getSimServiceTable(int subId, int appType) throws RemoteException { |
| return callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getSimServiceTable", |
| (phone) -> { |
| enforceTelephonyFeatureWithException(getCurrentPackageName(), |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSimServiceTable"); |
| |
| UiccPort uiccPort = phone.getUiccPort(); |
| if (uiccPort == null || uiccPort.getUiccProfile() == null) { |
| loge("getSimServiceTable(): uiccPort or uiccProfile is null"); |
| return null; |
| } |
| UiccCardApplication uiccApp = uiccPort.getUiccProfile().getApplicationByType( |
| appType); |
| if (uiccApp == null) { |
| loge("getSimServiceTable(): no app with specified apptype=" |
| + appType); |
| return null; |
| } |
| return ((SIMRecords)uiccApp.getIccRecords()).getSimServiceTable(); |
| }); |
| } |
| |
| @Override |
| public String getIccSimChallengeResponse(int subId, int appType, int authType, String data, |
| String callingPackage, String callingFeatureId) throws RemoteException { |
| CallPhoneMethodHelper<String> toExecute = (phone)-> { |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getIccSimChallengeResponse"); |
| |
| UiccPort uiccPort = phone.getUiccPort(); |
| if (uiccPort == null) { |
| loge("getIccSimChallengeResponse() uiccPort is null"); |
| return null; |
| } |
| |
| UiccCardApplication uiccApp = uiccPort.getApplicationByType(appType); |
| if (uiccApp == null) { |
| loge("getIccSimChallengeResponse() no app with specified type -- " + appType); |
| return null; |
| } else { |
| loge("getIccSimChallengeResponse() found app " + uiccApp.getAid() |
| + " specified type -- " + appType); |
| } |
| |
| if (authType != UiccCardApplication.AUTH_CONTEXT_EAP_SIM |
| && authType != UiccCardApplication.AUTH_CONTEXT_EAP_AKA |
| && authType != UiccCardApplication.AUTH_CONTEXT_GBA_BOOTSTRAP |
| && authType != UiccCardApplication.AUTHTYPE_GBA_NAF_KEY_EXTERNAL) { |
| loge("getIccSimChallengeResponse() unsupported authType: " + authType); |
| return null; |
| } |
| return uiccApp.getIccRecords().getIccSimChallengeResponse(authType, data); |
| }; |
| |
| return callPhoneMethodWithPermissionCheck(subId, callingPackage, callingFeatureId, |
| "getIccSimChallengeResponse", toExecute, |
| this::enforceIccSimChallengeResponsePermission); |
| } |
| |
| public String getGroupIdLevel1ForSubscriber(int subId, String callingPackage, |
| String callingFeatureId) { |
| return callPhoneMethodForSubIdWithReadCheck(subId, callingPackage, callingFeatureId, |
| "getGroupIdLevel1", (phone)-> { |
| enforceTelephonyFeatureWithException(callingPackage, |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, |
| "getGroupIdLevel1ForSubscriber"); |
| |
| return phone.getGroupIdLevel1(); |
| }); |
| } |
| |
| /** Below are utility methods that abstracts the flow that many public methods use: |
| * 1. Check permission: pass, throw exception, or fails (returns false). |
| * 2. clearCallingIdentity. |
| * 3. Call a specified phone method and get return value. |
| * 4. restoreCallingIdentity and return. |
| */ |
| private interface CallPhoneMethodHelper<T> { |
| T callMethod(Phone phone); |
| } |
| |
| private interface PermissionCheckHelper { |
| // Implemented to do whatever permission check it wants. |
| // If passes, it should return true. |
| // If permission is not granted, throws SecurityException. |
| // If permission is revoked by AppOps, return false. |
| boolean checkPermission(Context context, int subId, String callingPackage, |
| @Nullable String callingFeatureId, String message); |
| } |
| |
| // Base utility method that others use. |
| private <T> T callPhoneMethodWithPermissionCheck(int subId, String callingPackage, |
| @Nullable String callingFeatureId, String message, |
| CallPhoneMethodHelper<T> callMethodHelper, |
| PermissionCheckHelper permissionCheckHelper) { |
| if (!permissionCheckHelper.checkPermission(mContext, subId, callingPackage, |
| callingFeatureId, message)) { |
| return null; |
| } |
| |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| Phone phone = getPhone(subId); |
| if (phone != null) { |
| return callMethodHelper.callMethod(phone); |
| } else { |
| if (VDBG) loge(message + " phone is null for Subscription:" + subId); |
| return null; |
| } |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| private <T> T callPhoneMethodForSubIdWithReadCheck(int subId, String callingPackage, |
| @Nullable String callingFeatureId, String message, |
| CallPhoneMethodHelper<T> callMethodHelper) { |
| return callPhoneMethodWithPermissionCheck(subId, callingPackage, callingFeatureId, |
| message, callMethodHelper, |
| (aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage)-> |
| TelephonyPermissions.checkCallingOrSelfReadPhoneState( |
| aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage)); |
| } |
| |
| private <T> T callPhoneMethodForSubIdWithReadDeviceIdentifiersCheck(int subId, |
| String callingPackage, @Nullable String callingFeatureId, String message, |
| CallPhoneMethodHelper<T> callMethodHelper) { |
| return callPhoneMethodWithPermissionCheck(subId, callingPackage, callingFeatureId, |
| message, callMethodHelper, |
| (aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage)-> |
| TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers( |
| aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage)); |
| } |
| |
| private <T> T callPhoneMethodForSubIdWithReadSubscriberIdentifiersCheck(int subId, |
| String callingPackage, @Nullable String callingFeatureId, String message, |
| CallPhoneMethodHelper<T> callMethodHelper) { |
| return callPhoneMethodWithPermissionCheck(subId, callingPackage, callingFeatureId, |
| message, callMethodHelper, |
| (aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage)-> |
| TelephonyPermissions.checkCallingOrSelfReadSubscriberIdentifiers( |
| aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage)); |
| } |
| |
| private <T> T callPhoneMethodForSubIdWithPrivilegedCheck( |
| int subId, String message, CallPhoneMethodHelper<T> callMethodHelper) { |
| return callPhoneMethodWithPermissionCheck(subId, null, null, message, callMethodHelper, |
| (aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage) -> { |
| mContext.enforceCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE, message); |
| return true; |
| }); |
| } |
| |
| private <T> T callPhoneMethodForSubIdWithModifyCheck(int subId, String callingPackage, |
| String message, CallPhoneMethodHelper<T> callMethodHelper) { |
| return callPhoneMethodWithPermissionCheck(subId, null, null, message, callMethodHelper, |
| (aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage)-> { |
| enforceModifyPermission(); |
| return true; |
| }); |
| } |
| |
| private <T> T callPhoneMethodForSubIdWithReadPhoneNumberCheck(int subId, String callingPackage, |
| @NonNull String callingFeatureId, String message, |
| CallPhoneMethodHelper<T> callMethodHelper) { |
| return callPhoneMethodWithPermissionCheck(subId, callingPackage, callingFeatureId, |
| message, callMethodHelper, |
| (aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage) -> |
| TelephonyPermissions.checkCallingOrSelfReadPhoneNumber( |
| aContext, aSubId, aCallingPackage, aCallingFeatureId, aMessage)); |
| } |
| |
| private <T> T callPhoneMethodForPhoneIdWithReadDeviceIdentifiersCheck(int phoneId, |
| String callingPackage, @Nullable String callingFeatureId, String message, |
| CallPhoneMethodHelper<T> callMethodHelper) { |
| // Getting subId before doing permission check. |
| if (!SubscriptionManager.isValidPhoneId(phoneId)) { |
| phoneId = 0; |
| } |
| final Phone phone = PhoneFactory.getPhone(phoneId); |
| if (phone == null) { |
| return null; |
| } |
| if (!TelephonyPermissions.checkCallingOrSelfReadDeviceIdentifiers(mContext, |
| phone.getSubId(), callingPackage, callingFeatureId, message)) { |
| return null; |
| } |
| |
| final long identity = Binder.clearCallingIdentity(); |
| try { |
| return callMethodHelper.callMethod(phone); |
| } finally { |
| Binder.restoreCallingIdentity(identity); |
| } |
| } |
| |
| /** |
| * Returns SIP URI or tel URI of the Public Service Identity of the SM-SC fetched from |
| * EF_PSISMSC elementary field as defined in Section 4.5.9 (3GPP TS 31.102). |
| * @throws IllegalStateException in case if phone or UiccApplication is not available. |
| */ |
| public Uri getSmscIdentity(int subId, int appType) throws RemoteException { |
| Uri smscIdentityUri = callPhoneMethodForSubIdWithPrivilegedCheck(subId, "getSmscIdentity", |
| (phone) -> { |
| enforceTelephonyFeatureWithException(getCurrentPackageName(), |
| PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION, "getSmscIdentity"); |
| |
| try { |
| String smscIdentity = null; |
| UiccPort uiccPort = phone.getUiccPort(); |
| UiccCardApplication uiccApp = |
| uiccPort.getUiccProfile().getApplicationByType( |
| appType); |
| smscIdentity = (uiccApp != null) ? uiccApp.getIccRecords().getSmscIdentity() |
| : null; |
| if (TextUtils.isEmpty(smscIdentity)) { |
| return Uri.EMPTY; |
| } |
| return Uri.parse(smscIdentity); |
| } catch (NullPointerException ex) { |
| Rlog.e(TAG, "getSmscIdentity(): Exception = " + ex); |
| return null; |
| } |
| }); |
| if (smscIdentityUri == null) { |
| throw new IllegalStateException("Telephony service error"); |
| } |
| return smscIdentityUri; |
| } |
| |
| /** |
| * Get the current calling package name. |
| * @return the current calling package name |
| */ |
| @Nullable |
| private String getCurrentPackageName() { |
| if (mPackageManager == null) return null; |
| String[] callingUids = mPackageManager.getPackagesForUid(Binder.getCallingUid()); |
| return (callingUids == null) ? null : callingUids[0]; |
| } |
| |
| /** |
| * Make sure the device has required telephony feature |
| * |
| * @throws UnsupportedOperationException if the device does not have required telephony feature |
| */ |
| private void enforceTelephonyFeatureWithException(@Nullable String callingPackage, |
| @NonNull String telephonyFeature, @NonNull String methodName) { |
| if (callingPackage == null || mPackageManager == null) { |
| return; |
| } |
| |
| if (!mFeatureFlags.enforceTelephonyFeatureMappingForPublicApis() |
| || !CompatChanges.isChangeEnabled(ENABLE_FEATURE_MAPPING, callingPackage, |
| Binder.getCallingUserHandle()) |
| || mVendorApiLevel < Build.VERSION_CODES.VANILLA_ICE_CREAM) { |
| // Skip to check associated telephony feature, |
| // if compatibility change is not enabled for the current process or |
| // the SDK version of vendor partition is less than Android V. |
| return; |
| } |
| |
| if (!mPackageManager.hasSystemFeature(telephonyFeature)) { |
| throw new UnsupportedOperationException( |
| methodName + " is unsupported without " + telephonyFeature); |
| } |
| } |
| |
| private void log(String s) { |
| Rlog.d(TAG, s); |
| } |
| |
| @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) |
| private void loge(String s) { |
| Rlog.e(TAG, s); |
| } |
| } |