• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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