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