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 }