1 /* 2 * Copyright (C) 2018 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 17 package com.android.ons; 18 19 import android.app.PendingIntent; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.os.Handler; 23 import android.os.Looper; 24 import android.os.Message; 25 import android.os.ParcelUuid; 26 import android.os.PersistableBundle; 27 import android.telephony.CarrierConfigManager; 28 import android.telephony.SubscriptionInfo; 29 import android.telephony.SubscriptionManager; 30 import android.telephony.euicc.EuiccManager; 31 import android.util.Log; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 38 /** 39 * @class ONSProfileConfigurator 40 * @brief Helper class to support ONSProfileActivator to read and update profile, operator and CBRS 41 * configurations. 42 */ 43 public class ONSProfileConfigurator { 44 45 private static final String TAG = ONSProfileConfigurator.class.getName(); 46 @VisibleForTesting protected static final String PARAM_SUB_ID = "SUB_ID"; 47 @VisibleForTesting protected static final String PARAM_REQUEST_TYPE = "REQUEST_TYPE"; 48 @VisibleForTesting protected static final int REQUEST_CODE_ACTIVATE_SUB = 1; 49 @VisibleForTesting protected static final int REQUEST_CODE_DELETE_SUB = 2; 50 @VisibleForTesting 51 protected static final String ACTION_ONS_ESIM_CONFIG = "com.android.ons.action.ESIM_CONFIG"; 52 53 private final Context mContext; 54 private final SubscriptionManager mSubscriptionManager; 55 private final CarrierConfigManager mCarrierConfigManager; 56 private final EuiccManager mEuiccManager; 57 private ONSProfConfigListener mONSProfConfigListener = null; 58 private final Handler mHandler; 59 ONSProfileConfigurator(Context context, SubscriptionManager subscriptionManager, CarrierConfigManager carrierConfigManager, EuiccManager euiccManager, ONSProfConfigListener listener)60 public ONSProfileConfigurator(Context context, SubscriptionManager subscriptionManager, 61 CarrierConfigManager carrierConfigManager, 62 EuiccManager euiccManager, ONSProfConfigListener listener) { 63 mContext = context; 64 mSubscriptionManager = subscriptionManager; 65 mCarrierConfigManager = carrierConfigManager; 66 mEuiccManager = euiccManager; 67 mONSProfConfigListener = listener; 68 69 mHandler = new Handler(Looper.myLooper()) { 70 @Override 71 public void handleMessage(Message msg) { 72 super.handleMessage(msg); 73 callbackMsgHandler(msg); 74 } 75 }; 76 } 77 78 /** 79 * Callback to receive result for subscription activate request and process the same. 80 * 81 * @param intent 82 * @param resultCode 83 */ onCallbackIntentReceived(Intent intent, int resultCode)84 public void onCallbackIntentReceived(Intent intent, int resultCode) { 85 Message msg = new Message(); 86 msg.obj = intent; 87 msg.arg1 = resultCode; 88 mHandler.sendMessage(msg); 89 } 90 91 @VisibleForTesting callbackMsgHandler(Message msg)92 protected void callbackMsgHandler(Message msg) { 93 Intent intent = (Intent) msg.obj; 94 int resultCode = msg.arg1; 95 96 int reqCode = intent.getIntExtra(PARAM_REQUEST_TYPE, 0); 97 switch (reqCode) { 98 case REQUEST_CODE_ACTIVATE_SUB: { 99 /*int subId = intent.getIntExtra(ACTIVATE_SUB_ID, 0);*/ 100 Log.d(TAG, "Opportunistic subscription activated successfully. SubId:" 101 + intent.getIntExtra(PARAM_SUB_ID, 0)); 102 Log.d(TAG, "Detailed result code: " + intent.getIntExtra( 103 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0)); 104 } 105 break; 106 case REQUEST_CODE_DELETE_SUB: { 107 if (resultCode != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) { 108 Log.e(TAG, "Error removing euicc opportunistic profile." 109 + "Detailed error code = " + intent.getIntExtra( 110 EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0)); 111 } else if (mONSProfConfigListener != null) { 112 int subId = intent.getIntExtra(PARAM_SUB_ID, 0); 113 mONSProfConfigListener.onOppSubscriptionDeleted(subId); 114 Log.d(TAG, "Opportunistic subscription deleted successfully. Id:" + subId); 115 } 116 } 117 break; 118 } 119 }; 120 121 /** 122 * Adds downloaded subscription to the group, activates and enables opportunistic data. 123 * 124 * @param opportunisticESIM 125 * @param groupUuid 126 */ 127 @VisibleForTesting groupWithPSIMAndSetOpportunistic( SubscriptionInfo opportunisticESIM, ParcelUuid groupUuid)128 protected void groupWithPSIMAndSetOpportunistic( 129 SubscriptionInfo opportunisticESIM, ParcelUuid groupUuid) { 130 if (groupUuid != null && groupUuid.equals(opportunisticESIM.getGroupUuid())) { 131 Log.d(TAG, "opportunistc eSIM and CBRS pSIM already grouped"); 132 } else { 133 Log.d(TAG, "Grouping opportunistc eSIM and CBRS pSIM"); 134 ArrayList<Integer> subList = new ArrayList<>(); 135 subList.add(opportunisticESIM.getSubscriptionId()); 136 mSubscriptionManager.addSubscriptionsIntoGroup(subList, groupUuid); 137 } 138 139 if (!opportunisticESIM.isOpportunistic()) { 140 Log.d(TAG, "set Opportunistic to TRUE"); 141 mSubscriptionManager.setOpportunistic(true, 142 opportunisticESIM.getSubscriptionId()); 143 } 144 //activateSubscription(opportunisticESIM);// -> activate after download flag is passed as 145 //true in download request so no need of explicit activation. 146 } 147 148 /** 149 * Activates provided subscription. Result is received in method onCallbackIntentReceived. 150 */ activateSubscription(int subId)151 public void activateSubscription(int subId) { 152 Intent intent = new Intent(mContext, ONSProfileResultReceiver.class); 153 intent.setAction(ACTION_ONS_ESIM_CONFIG); 154 intent.putExtra(PARAM_REQUEST_TYPE, REQUEST_CODE_ACTIVATE_SUB); 155 intent.putExtra(PARAM_SUB_ID, subId); 156 PendingIntent callbackIntent = PendingIntent.getBroadcast(mContext, 157 REQUEST_CODE_ACTIVATE_SUB, intent, PendingIntent.FLAG_IMMUTABLE); 158 Log.d(TAG, "Activate oppSub request sent to SubManager"); 159 mEuiccManager.switchToSubscription(subId, callbackIntent); 160 } 161 162 /** 163 * Deletes inactive opportunistic subscriptions irrespective of the CBRS operator. 164 * Called when sufficient memory is not available before downloading new profile. 165 */ deleteInactiveOpportunisticSubscriptions(int pSIMId)166 public boolean deleteInactiveOpportunisticSubscriptions(int pSIMId) { 167 Log.d(TAG, "deleteInactiveOpportunisticSubscriptions"); 168 169 List<SubscriptionInfo> subList = mSubscriptionManager.getOpportunisticSubscriptions(); 170 if (subList == null || subList.size() <= 0) { 171 return false; 172 } 173 174 for (SubscriptionInfo subInfo : subList) { 175 int subId = subInfo.getSubscriptionId(); 176 if (!mSubscriptionManager.isActiveSubscriptionId(subId)) { 177 deleteSubscription(subId); 178 return true; 179 } 180 } 181 182 return false; 183 } 184 185 /** 186 * Returns previously downloaded opportunistic eSIM associated with pSIM CBRS operator. 187 * Helpful to cleanup before downloading new opportunistic eSIM from the same CBRS operator. 188 * 189 * @return true - If an eSIM is found. 190 * false - If no eSIM is found. 191 */ getOpportunisticSubIdsofPSIMOperator(int pSIMSubId)192 ArrayList<Integer> getOpportunisticSubIdsofPSIMOperator(int pSIMSubId) { 193 Log.d(TAG, "getOpportunisticSubIdsofPSIMOperator"); 194 ArrayList<Integer> opportunisticSubIds = new ArrayList<Integer>(); 195 //1.Get the list of all opportunistic carrier-ids of newly inserted pSIM from carrier config 196 PersistableBundle config = mCarrierConfigManager.getConfigForSubId(pSIMSubId); 197 int[] oppCarrierIdArr = config.getIntArray( 198 CarrierConfigManager.KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY); 199 if (oppCarrierIdArr == null || oppCarrierIdArr.length <= 0) { 200 return null; 201 } 202 203 //2. Get list of all subscriptions 204 List<SubscriptionInfo> oppSubList = mSubscriptionManager.getAvailableSubscriptionInfoList(); 205 for (SubscriptionInfo subInfo : oppSubList) { 206 for (int oppCarrierId : oppCarrierIdArr) { 207 //Carrier-id of opportunistic eSIM matches with one of thecarrier-ids in carrier 208 // config of pSIM 209 if (subInfo.isEmbedded() && oppCarrierId == subInfo 210 .getCarrierId()) { 211 //3.if carrier-id of eSIM matches with one of the pSIM opportunistic carrier-ids 212 //and eSIM's pSIM carrier-id matches with new pSIM then delete the subscription 213 opportunisticSubIds.add(subInfo.getSubscriptionId()); 214 } 215 } 216 } 217 218 return opportunisticSubIds; 219 } 220 221 /** 222 * Sends delete request to the eUICC manager to delete a given subscription. 223 * @param subId 224 */ deleteSubscription(int subId)225 public void deleteSubscription(int subId) { 226 Log.d(TAG, "deleting subscription. SubId: " + subId); 227 Intent intent = new Intent(mContext, ONSProfileResultReceiver.class); 228 intent.setAction(ACTION_ONS_ESIM_CONFIG); 229 intent.putExtra(PARAM_REQUEST_TYPE, REQUEST_CODE_DELETE_SUB); 230 intent.putExtra(PARAM_SUB_ID, subId); 231 PendingIntent callbackIntent = PendingIntent.getBroadcast(mContext, 232 REQUEST_CODE_DELETE_SUB, intent, PendingIntent.FLAG_MUTABLE); 233 mEuiccManager.deleteSubscription(subId, callbackIntent); 234 } 235 236 /** 237 * Creates Subscription Group for PSIM if it doesn't exist or returns existing group-id. 238 */ getPSIMGroupId(SubscriptionInfo primaryCBRSSubInfo)239 public ParcelUuid getPSIMGroupId(SubscriptionInfo primaryCBRSSubInfo) { 240 ParcelUuid groupId = primaryCBRSSubInfo.getGroupUuid(); 241 if (groupId != null) { 242 return groupId; 243 } 244 245 Log.d(TAG, "Creating Group for Primary SIM"); 246 List<Integer> pSubList = new ArrayList<>(); 247 pSubList.add(primaryCBRSSubInfo.getSubscriptionId()); 248 return mSubscriptionManager.createSubscriptionGroup(pSubList); 249 } 250 251 /** 252 * Searches for opportunistic profile in all available subscriptions using carrier-ids 253 * from carrier configuration and returns opportunistic subscription. 254 */ findOpportunisticSubscription(int pSIMId)255 public SubscriptionInfo findOpportunisticSubscription(int pSIMId) { 256 Log.d(TAG, "findOpportunisticSubscription. PSIM Id : " + pSIMId); 257 258 //Get the list of active subscriptions 259 List<SubscriptionInfo> availSubInfoList = mSubscriptionManager 260 .getAvailableSubscriptionInfoList(); 261 if (availSubInfoList == null) { 262 Log.e(TAG, "getAvailableSubscriptionInfoList returned null"); 263 return null; 264 } 265 Log.d(TAG, "Available subscriptions: " + availSubInfoList.size()); 266 267 //Get the list of opportunistic carrier-ids list from carrier config. 268 PersistableBundle config = mCarrierConfigManager.getConfigForSubId(pSIMId); 269 int[] oppCarrierIdArr = config.getIntArray( 270 CarrierConfigManager.KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY); 271 if (oppCarrierIdArr == null || oppCarrierIdArr.length <= 0) { 272 Log.e(TAG, "Opportunistic carrier-ids list is empty in carrier config"); 273 return null; 274 } 275 276 SubscriptionInfo subscriptionInfo = mSubscriptionManager.getActiveSubscriptionInfo(pSIMId); 277 if (subscriptionInfo == null) { 278 Log.e(TAG, "getActiveSubscriptionInfo returned null for: " + pSIMId); 279 return null; 280 } 281 ParcelUuid pSIMSubGroupId = subscriptionInfo.getGroupUuid(); 282 for (SubscriptionInfo subInfo : availSubInfoList) { 283 if (subInfo.getSubscriptionId() != pSIMId) { 284 for (int carrId : oppCarrierIdArr) { 285 if (subInfo.isEmbedded() && carrId == subInfo.getCarrierId()) { 286 //An active eSIM whose carrier-id is listed as opportunistic carrier in 287 // carrier config is newly downloaded opportunistic eSIM. 288 289 ParcelUuid oppSubGroupId = subInfo.getGroupUuid(); 290 if (oppSubGroupId == null /*Immediately after opp eSIM is downloaded case*/ 291 || oppSubGroupId.equals(pSIMSubGroupId) /*Already downloaded and 292 grouped case.*/) { 293 Log.d(TAG, "Opp subscription:" + subInfo.getSubscriptionId()); 294 return subInfo; 295 } 296 } 297 } 298 } 299 } 300 301 return null; 302 } 303 304 /** 305 * Listener interface to notify delete subscription operation. 306 */ 307 public interface ONSProfConfigListener { 308 /** 309 * Called when the delete subscription request is processed successfully. 310 */ onOppSubscriptionDeleted(int pSIMId)311 void onOppSubscriptionDeleted(int pSIMId); 312 } 313 } 314