1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.nfc.cardemulation.util; 17 18 import android.content.Context; 19 import android.telephony.SubscriptionInfo; 20 import android.telephony.SubscriptionManager; 21 import android.telephony.TelephonyManager; 22 import android.util.Log; 23 24 import java.util.Collections; 25 import java.util.List; 26 import java.util.Optional; 27 import java.util.concurrent.Executors; 28 import java.util.concurrent.atomic.AtomicReference; 29 import java.util.function.Predicate; 30 31 public class TelephonyUtils extends SubscriptionManager.OnSubscriptionsChangedListener{ 32 private final String TAG = "TelephonyUtils"; 33 public static final int SUBSCRIPTION_STATE_UNKNOWN = -1; 34 public static final int SUBSCRIPTION_STATE_ACTIVATE = 1; 35 public static final int SUBSCRIPTION_STATE_INACTIVATE = 2; 36 37 public static final int SUBSCRIPTION_ID_UNKNOWN = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 38 public static final int SUBSCRIPTION_ID_UICC = 0x100000; 39 public static final int PORT_IS_NOT_SET = 0xFF; 40 41 public static final int SIM_TYPE_UNKNOWN = 0; 42 public static final int SIM_TYPE_UICC = 1; 43 public static final int SIM_TYPE_EUICC_1 = 2; 44 public static final int SIM_TYPE_EUICC_2 = 3; 45 46 public static final int MEP_MODE_NONE = 0; 47 public static final int MEP_MODE_A1 = 1; 48 public static final int MEP_MODE_A2 = 2; 49 public static final int MEP_MODE_B = 3; 50 51 private TelephonyManager mTelephonyManager; 52 private SubscriptionManager mSubscriptionManager; 53 54 private boolean mIsSubscriptionsChangedListenerRegistered = false; 55 56 public static Predicate<SubscriptionInfo> SUBSCRIPTION_ACTIVE_CONDITION_FOR_UICC = 57 subscriptionInfo -> !subscriptionInfo.isEmbedded() 58 && subscriptionInfo.areUiccApplicationsEnabled(); 59 public static Predicate<SubscriptionInfo> SUBSCRIPTION_ACTIVE_CONDITION_FOR_EUICC = 60 subscriptionInfo -> subscriptionInfo.isEmbedded(); 61 62 int mMepMode ; // TODO - How to set MEP mode. Port value is depending on MEP Mode 63 public interface Callback { onActiveSubscriptionsUpdated(List<SubscriptionInfo> activeSubscriptionList)64 void onActiveSubscriptionsUpdated(List<SubscriptionInfo> activeSubscriptionList); 65 } 66 Callback mCallback; 67 private static class Singleton { 68 private static TelephonyUtils sInstance = null; 69 } 70 getInstance(Context context)71 public static TelephonyUtils getInstance(Context context) { 72 if (TelephonyUtils.Singleton.sInstance == null) { 73 TelephonyUtils.Singleton.sInstance = new TelephonyUtils(context); 74 } 75 return TelephonyUtils.Singleton.sInstance; 76 } 77 TelephonyUtils(Context context)78 public TelephonyUtils(Context context) { 79 mTelephonyManager = context.getSystemService(TelephonyManager.class); 80 mSubscriptionManager = SubscriptionManager.from(context); 81 mMepMode = MEP_MODE_B; //TODO - How to check MEP mode from eSIM 82 } 83 registerSubscriptionChangedCallback(Callback callback)84 public void registerSubscriptionChangedCallback(Callback callback) { 85 mCallback = callback; 86 mSubscriptionManager.addOnSubscriptionsChangedListener( 87 Executors.newSingleThreadExecutor(), this); 88 } 89 getActiveSubscriptionInfoById(int subscriptionId)90 public Optional<SubscriptionInfo> getActiveSubscriptionInfoById(int subscriptionId) { 91 Log.d(TAG, "getActiveSubscriptionInfoById: " + subscriptionId); 92 if (isUiccSubscription(subscriptionId)) { 93 Log.d(TAG, "getActiveSubscriptionInfoById: Uicc Subscription"); 94 return findFirstActiveSubscriptionInfo(subscriptionInfo -> 95 !subscriptionInfo.isEmbedded() 96 && subscriptionInfo.areUiccApplicationsEnabled()); 97 } 98 else { 99 Log.d(TAG, "getActiveSubscriptionInfoById: Embedded Uicc Subscription"); 100 return Optional.ofNullable( 101 mSubscriptionManager.getActiveSubscriptionInfo(subscriptionId)); 102 } 103 } isUiccSubscription(int subscriptionId)104 public boolean isUiccSubscription(int subscriptionId) { 105 return subscriptionId == SUBSCRIPTION_ID_UICC; 106 } 107 isEuiccSubscription(int subscriptionId)108 public boolean isEuiccSubscription(int subscriptionId) { 109 return (subscriptionId != SUBSCRIPTION_ID_UICC) && 110 (subscriptionId != SUBSCRIPTION_ID_UNKNOWN); 111 } 112 getActiveSubscriptions()113 public List<SubscriptionInfo> getActiveSubscriptions() { 114 List<SubscriptionInfo> list = mSubscriptionManager.getActiveSubscriptionInfoList(); 115 return (list != null) ? list : Collections.emptyList(); 116 } 117 118 @Override onSubscriptionsChanged()119 public void onSubscriptionsChanged() { 120 Log.d(TAG, "onSubscriptionsChanged"); 121 if (!mIsSubscriptionsChangedListenerRegistered) { 122 Log.d(TAG, "onSubscriptionsChanged: Skip when receive the event with registering"); 123 mIsSubscriptionsChangedListenerRegistered = true; 124 return; 125 } 126 127 mCallback.onActiveSubscriptionsUpdated( 128 mSubscriptionManager.getActiveSubscriptionInfoList()); 129 } 130 updateSwpStatusForEuicc(int simType)131 public String updateSwpStatusForEuicc(int simType) { 132 return transmitApduToActiveSubscription(0x80, 0x7C, 0x02, 133 getPort(simType), 0x00, ""); 134 } 135 transmitApduToActiveSubscription( int cla, int ins, int p1, int p2, int p3, String data)136 private String transmitApduToActiveSubscription( 137 int cla, int ins, int p1, int p2, int p3, String data) { 138 AtomicReference<String > response = new AtomicReference<>(); 139 findFirstActiveSubscriptionInfo(SUBSCRIPTION_ACTIVE_CONDITION_FOR_EUICC) 140 .ifPresentOrElse( 141 subscriptionInfo -> { 142 String result = mTelephonyManager 143 .createForSubscriptionId(subscriptionInfo.getSubscriptionId()) 144 .iccTransmitApduBasicChannel(cla, ins, p1, p2, p3, data); 145 response.set(result); 146 } , 147 ()-> { 148 Log.d(TAG, "transmitApduToActiveSubscription: Send APDU fail because " 149 + "active subscription is not exist"); 150 response.set("FFFF"); 151 } 152 ); 153 return response.get(); 154 } 155 findFirstActiveSubscriptionInfo( Predicate<SubscriptionInfo> condition)156 private Optional<SubscriptionInfo> findFirstActiveSubscriptionInfo( 157 Predicate<SubscriptionInfo> condition) { 158 return getActiveSubscriptions().stream().filter(condition) 159 .findFirst(); 160 } 161 162 // TODO - port value updated according to MEP Mode 163 // Currently, Nfc could not know MEP mode. getPort(int simType)164 private int getPort(int simType) { 165 int port = PORT_IS_NOT_SET; 166 if (mMepMode != MEP_MODE_NONE) { 167 if (simType == SIM_TYPE_EUICC_1) { 168 port = (mMepMode == MEP_MODE_B) ? 0x00 : 0x01; 169 } else if (simType == SIM_TYPE_EUICC_2) { 170 port = (mMepMode == MEP_MODE_B) ? 0x01 : 0x02; 171 } 172 } 173 return port; 174 } 175 setMepMode(int mepMode)176 public void setMepMode(int mepMode) { 177 mMepMode = mepMode; 178 } 179 180 181 } 182