1 /* 2 * Copyright (C) 2021 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.settings.network.helper; 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 androidx.annotation.Keep; 25 import androidx.annotation.VisibleForTesting; 26 27 import com.android.settings.network.helper.SubscriptionAnnotation; 28 import com.android.settingslib.utils.ThreadUtils; 29 30 import java.util.Collections; 31 import java.util.List; 32 import java.util.concurrent.Callable; 33 import java.util.concurrent.Future; 34 import java.util.concurrent.atomic.AtomicIntegerArray; 35 import java.util.function.Function; 36 import java.util.function.Predicate; 37 import java.util.function.Supplier; 38 import java.util.function.UnaryOperator; 39 import java.util.stream.Collectors; 40 import java.util.stream.IntStream; 41 42 /** 43 * This is a Callable class to query user selectable subscription list. 44 * 45 * Here's example of creating a Callable for retrieving a list of SubscriptionAnnotation 46 * for active Subscriptions: 47 * 48 * List<SubscriptionAnnotation> result = (new SelectableSubscriptions(context, false)).call(); 49 * 50 * Another example for retrieving a list of SubscriptionAnnotation for all subscriptions 51 * accessible in another thread. 52 * 53 * List<SubscriptionAnnotation> result = ExecutorService.submit( 54 * new SelectableSubscriptions(context, true)).get() 55 */ 56 public class SelectableSubscriptions implements Callable<List<SubscriptionAnnotation>> { 57 private static final String TAG = "SelectableSubscriptions"; 58 59 private Context mContext; 60 private Supplier<List<SubscriptionInfo>> mSubscriptions; 61 private Predicate<SubscriptionAnnotation> mFilter; 62 private Function<List<SubscriptionAnnotation>, List<SubscriptionAnnotation>> mFinisher; 63 64 /** 65 * Constructor of class 66 * @param context 67 * @param disabledSlotsIncluded query both active and inactive slots when true, 68 * only query active slot when false. 69 */ SelectableSubscriptions(Context context, boolean disabledSlotsIncluded)70 public SelectableSubscriptions(Context context, boolean disabledSlotsIncluded) { 71 mContext = context; 72 mSubscriptions = disabledSlotsIncluded ? (() -> getAvailableSubInfoList(context)) : 73 (() -> getActiveSubInfoList(context)); 74 if (disabledSlotsIncluded) { 75 mFilter = subAnno -> { 76 if (subAnno.isExisted()) { 77 return true; 78 } 79 return ((subAnno.getType() == SubscriptionAnnotation.TYPE_ESIM) 80 && (subAnno.isDisplayAllowed())); 81 }; 82 } else { 83 mFilter = subAnno -> subAnno.isActive(); 84 } 85 mFinisher = annoList -> annoList; 86 } 87 88 /** 89 * Add UnaryOperator to be applied to the final result. 90 * @param finisher a function to be applied to the final result. 91 */ addFinisher( UnaryOperator<List<SubscriptionAnnotation>> finisher)92 public SelectableSubscriptions addFinisher( 93 UnaryOperator<List<SubscriptionAnnotation>> finisher) { 94 mFinisher = mFinisher.andThen(finisher); 95 return this; 96 } 97 98 /** 99 * Implementation of Callable 100 * @return a list of SubscriptionAnnotation which is user selectable 101 */ call()102 public List<SubscriptionAnnotation> call() { 103 TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class); 104 105 try { 106 // query in background thread 107 Future<AtomicIntegerArray> eSimCardId = 108 ThreadUtils.postOnBackgroundThread(new QueryEsimCardId(telMgr)); 109 110 // query in background thread 111 Future<AtomicIntegerArray> simSlotIndex = 112 ThreadUtils.postOnBackgroundThread( 113 new QuerySimSlotIndex(telMgr, true, true)); 114 115 // query in background thread 116 Future<AtomicIntegerArray> activeSimSlotIndex = 117 ThreadUtils.postOnBackgroundThread( 118 new QuerySimSlotIndex(telMgr, false, true)); 119 120 List<SubscriptionInfo> subInfoList = mSubscriptions.get(); 121 122 // wait for result from background thread 123 List<Integer> eSimCardIdList = atomicToList(eSimCardId.get()); 124 List<Integer> simSlotIndexList = atomicToList(simSlotIndex.get()); 125 List<Integer> activeSimSlotIndexList = atomicToList(activeSimSlotIndex.get()); 126 127 // build a list of SubscriptionAnnotation 128 return IntStream.range(0, subInfoList.size()) 129 .mapToObj(subInfoIndex -> 130 new SubscriptionAnnotation.Builder(subInfoList, subInfoIndex)) 131 .map(annoBdr -> annoBdr.build(mContext, 132 eSimCardIdList, simSlotIndexList, activeSimSlotIndexList)) 133 .filter(mFilter) 134 .collect(Collectors.collectingAndThen(Collectors.toList(), mFinisher)); 135 } catch (Exception exception) { 136 Log.w(TAG, "Fail to request subIdList", exception); 137 } 138 return Collections.emptyList(); 139 } 140 getSubInfoList(Context context, Function<SubscriptionManager, List<SubscriptionInfo>> convertor)141 protected List<SubscriptionInfo> getSubInfoList(Context context, 142 Function<SubscriptionManager, List<SubscriptionInfo>> convertor) { 143 SubscriptionManager subManager = getSubscriptionManager(context); 144 return (subManager == null) ? Collections.emptyList() : convertor.apply(subManager); 145 } 146 getSubscriptionManager(Context context)147 protected SubscriptionManager getSubscriptionManager(Context context) { 148 return context.getSystemService(SubscriptionManager.class); 149 } 150 getAvailableSubInfoList(Context context)151 protected List<SubscriptionInfo> getAvailableSubInfoList(Context context) { 152 return getSubInfoList(context, SubscriptionManager::getAvailableSubscriptionInfoList); 153 } 154 getActiveSubInfoList(Context context)155 protected List<SubscriptionInfo> getActiveSubInfoList(Context context) { 156 return getSubInfoList(context, SubscriptionManager::getActiveSubscriptionInfoList); 157 } 158 159 @Keep 160 @VisibleForTesting atomicToList(AtomicIntegerArray atomicIntArray)161 protected static List<Integer> atomicToList(AtomicIntegerArray atomicIntArray) { 162 if (atomicIntArray == null) { 163 return Collections.emptyList(); 164 } 165 return IntStream.range(0, atomicIntArray.length()) 166 .map(idx -> atomicIntArray.get(idx)).boxed() 167 .collect(Collectors.toList()); 168 } 169 }