blob: 6026a9501e1217cbf36b5c9adf7109113b36148d [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* 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.ons;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.euicc.EuiccManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
/**
* @class ONSProfileConfigurator
* @brief Helper class to support ONSProfileActivator to read and update profile, operator and CBRS
* configurations.
*/
public class ONSProfileConfigurator {
private static final String TAG = ONSProfileConfigurator.class.getName();
@VisibleForTesting protected static final String PARAM_SUB_ID = "SUB_ID";
@VisibleForTesting protected static final String PARAM_REQUEST_TYPE = "REQUEST_TYPE";
@VisibleForTesting protected static final int REQUEST_CODE_ACTIVATE_SUB = 1;
@VisibleForTesting protected static final int REQUEST_CODE_DELETE_SUB = 2;
@VisibleForTesting
protected static final String ACTION_ONS_ESIM_CONFIG = "com.android.ons.action.ESIM_CONFIG";
private final Context mContext;
private final SubscriptionManager mSubscriptionManager;
private final CarrierConfigManager mCarrierConfigManager;
private final EuiccManager mEuiccManager;
private ONSProfConfigListener mONSProfConfigListener = null;
private final Handler mHandler;
public ONSProfileConfigurator(Context context, SubscriptionManager subscriptionManager,
CarrierConfigManager carrierConfigManager,
EuiccManager euiccManager, ONSProfConfigListener listener) {
mContext = context;
mSubscriptionManager = subscriptionManager;
mCarrierConfigManager = carrierConfigManager;
mEuiccManager = euiccManager;
mONSProfConfigListener = listener;
mHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
callbackMsgHandler(msg);
}
};
}
/**
* Callback to receive result for subscription activate request and process the same.
*
* @param intent
* @param resultCode
*/
public void onCallbackIntentReceived(Intent intent, int resultCode) {
Message msg = new Message();
msg.obj = intent;
msg.arg1 = resultCode;
mHandler.sendMessage(msg);
}
@VisibleForTesting
protected void callbackMsgHandler(Message msg) {
Intent intent = (Intent) msg.obj;
int resultCode = msg.arg1;
int reqCode = intent.getIntExtra(PARAM_REQUEST_TYPE, 0);
switch (reqCode) {
case REQUEST_CODE_ACTIVATE_SUB: {
/*int subId = intent.getIntExtra(ACTIVATE_SUB_ID, 0);*/
Log.d(TAG, "Opportunistic subscription activated successfully. SubId:"
+ intent.getIntExtra(PARAM_SUB_ID, 0));
Log.d(TAG, "Detailed result code: " + intent.getIntExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0));
}
break;
case REQUEST_CODE_DELETE_SUB: {
if (resultCode != EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
Log.e(TAG, "Error removing euicc opportunistic profile."
+ "Detailed error code = " + intent.getIntExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0));
} else if (mONSProfConfigListener != null) {
int subId = intent.getIntExtra(PARAM_SUB_ID, 0);
mONSProfConfigListener.onOppSubscriptionDeleted(subId);
Log.d(TAG, "Opportunistic subscription deleted successfully. Id:" + subId);
}
}
break;
}
};
/**
* Adds downloaded subscription to the group, activates and enables opportunistic data.
*
* @param opportunisticESIM
* @param groupUuid
*/
@VisibleForTesting
protected void groupWithPSIMAndSetOpportunistic(
SubscriptionInfo opportunisticESIM, ParcelUuid groupUuid) {
Log.d(TAG, "Grouping opportunistc eSIM and CBRS pSIM");
ArrayList<Integer> subList = new ArrayList<>();
subList.add(opportunisticESIM.getSubscriptionId());
mSubscriptionManager.addSubscriptionsIntoGroup(subList, groupUuid);
if (!opportunisticESIM.isOpportunistic()) {
Log.d(TAG, "set Opportunistic to TRUE");
mSubscriptionManager.setOpportunistic(true,
opportunisticESIM.getSubscriptionId());
}
//activateSubscription(opportunisticESIM);// -> activate after download flag is passed as
//true in download request so no need of explicit activation.
}
/**
* Activates provided subscription. Result is received in method onCallbackIntentReceived.
*/
public void activateSubscription(int subId) {
Intent intent = new Intent(mContext, ONSProfileResultReceiver.class);
intent.setAction(ACTION_ONS_ESIM_CONFIG);
intent.putExtra(PARAM_REQUEST_TYPE, REQUEST_CODE_ACTIVATE_SUB);
intent.putExtra(PARAM_SUB_ID, subId);
PendingIntent callbackIntent = PendingIntent.getBroadcast(mContext,
REQUEST_CODE_ACTIVATE_SUB, intent, PendingIntent.FLAG_IMMUTABLE);
Log.d(TAG, "Activate oppSub request sent to SubManager");
mSubscriptionManager.switchToSubscription(subId, callbackIntent);
}
/**
* Deletes inactive opportunistic subscriptions irrespective of the CBRS operator.
* Called when sufficient memory is not available before downloading new profile.
*/
public boolean deleteInactiveOpportunisticSubscriptions(int pSIMId) {
Log.d(TAG, "deleteInactiveOpportunisticSubscriptions");
List<SubscriptionInfo> subList = mSubscriptionManager.getOpportunisticSubscriptions();
if (subList == null || subList.size() <= 0) {
return false;
}
for (SubscriptionInfo subInfo : subList) {
int subId = subInfo.getSubscriptionId();
if (!mSubscriptionManager.isActiveSubscriptionId(subId)) {
deleteSubscription(subId);
return true;
}
}
return false;
}
/**
* Returns previously downloaded opportunistic eSIM associated with pSIM CBRS operator.
* Helpful to cleanup before downloading new opportunistic eSIM from the same CBRS operator.
*
* @return true - If an eSIM is found.
* false - If no eSIM is found.
*/
ArrayList<Integer> getOpportunisticSubIdsofPSIMOperator(int pSIMSubId) {
Log.d(TAG, "getOpportunisticSubIdsofPSIMOperator");
ArrayList<Integer> opportunisticSubIds = new ArrayList<Integer>();
//1.Get the list of all opportunistic carrier-ids of newly inserted pSIM from carrier config
PersistableBundle config = mCarrierConfigManager.getConfigForSubId(pSIMSubId);
int[] oppCarrierIdArr = config.getIntArray(
CarrierConfigManager.KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY);
if (oppCarrierIdArr == null || oppCarrierIdArr.length <= 0) {
return null;
}
//2. Get list of all subscriptions
List<SubscriptionInfo> oppSubList = mSubscriptionManager.getAvailableSubscriptionInfoList();
for (SubscriptionInfo subInfo : oppSubList) {
for (int oppCarrierId : oppCarrierIdArr) {
//Carrier-id of opportunistic eSIM matches with one of thecarrier-ids in carrier
// config of pSIM
if (subInfo.isEmbedded() && oppCarrierId == subInfo
.getCarrierId()) {
//3.if carrier-id of eSIM matches with one of the pSIM opportunistic carrier-ids
//and eSIM's pSIM carrier-id matches with new pSIM then delete the subscription
opportunisticSubIds.add(subInfo.getSubscriptionId());
}
}
}
return opportunisticSubIds;
}
/**
* Sends delete request to the eUICC manager to delete a given subscription.
* @param subId
*/
public void deleteSubscription(int subId) {
Log.d(TAG, "deleting subscription. SubId: " + subId);
Intent intent = new Intent(mContext, ONSProfileResultReceiver.class);
intent.setAction(ACTION_ONS_ESIM_CONFIG);
intent.putExtra(PARAM_REQUEST_TYPE, REQUEST_CODE_DELETE_SUB);
intent.putExtra(PARAM_SUB_ID, subId);
PendingIntent callbackIntent = PendingIntent.getBroadcast(mContext,
REQUEST_CODE_DELETE_SUB, intent, PendingIntent.FLAG_MUTABLE);
mEuiccManager.deleteSubscription(subId, callbackIntent);
}
/**
* Creates Subscription Group for PSIM if it doesn't exist or returns existing group-id.
*/
public ParcelUuid getPSIMGroupId(SubscriptionInfo primaryCBRSSubInfo) {
ParcelUuid groupId = primaryCBRSSubInfo.getGroupUuid();
if (groupId != null) {
return groupId;
}
Log.d(TAG, "Creating Group for Primary SIM");
List<Integer> pSubList = new ArrayList<>();
pSubList.add(primaryCBRSSubInfo.getSubscriptionId());
return mSubscriptionManager.createSubscriptionGroup(pSubList);
}
/**
* Searches for opportunistic profile in all available subscriptions using carrier-ids
* from carrier configuration and returns opportunistic subscription.
*/
public SubscriptionInfo findOpportunisticSubscription(int pSIMId) {
Log.d(TAG, "findOpportunisticSubscription. PSIM Id : " + pSIMId);
//Get the list of active subscriptions
List<SubscriptionInfo> availSubInfoList = mSubscriptionManager
.getAvailableSubscriptionInfoList();
Log.d(TAG, "Available subscriptions: " + availSubInfoList.size());
//Get the list of opportunistic carrier-ids list from carrier config.
PersistableBundle config = mCarrierConfigManager.getConfigForSubId(pSIMId);
int[] oppCarrierIdArr = config.getIntArray(
CarrierConfigManager.KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY);
if (oppCarrierIdArr == null || oppCarrierIdArr.length <= 0) {
Log.e(TAG, "Opportunistic carrier-ids list is empty in carrier config");
return null;
}
ParcelUuid pSIMSubGroupId = mSubscriptionManager.getActiveSubscriptionInfo(pSIMId)
.getGroupUuid();
for (SubscriptionInfo subInfo : availSubInfoList) {
if (subInfo.getSubscriptionId() != pSIMId) {
for (int carrId : oppCarrierIdArr) {
if (subInfo.isEmbedded() && carrId == subInfo.getCarrierId()) {
//An active eSIM whose carrier-id is listed as opportunistic carrier in
// carrier config is newly downloaded opportunistic eSIM.
ParcelUuid oppSubGroupId = subInfo.getGroupUuid();
if (oppSubGroupId == null /*Immediately after opp eSIM is downloaded case*/
|| oppSubGroupId.equals(pSIMSubGroupId) /*Already downloaded and
grouped case.*/) {
Log.d(TAG, "Opp subscription:" + subInfo.getSubscriptionId());
return subInfo;
}
}
}
}
}
return null;
}
/**
* Listener interface to notify delete subscription operation.
*/
public interface ONSProfConfigListener {
/**
* Called when the delete subscription request is processed successfully.
*/
void onOppSubscriptionDeleted(int pSIMId);
}
}