1 /*
2  * Copyright (C) 2020 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.services.telephony.rcs;
18 
19 import android.annotation.AnyThread;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.os.AsyncResult;
25 import android.os.Bundle;
26 import android.os.Handler;
27 import android.os.Looper;
28 import android.telephony.CarrierConfigManager;
29 import android.telephony.SubscriptionManager;
30 import android.util.Log;
31 import android.util.SparseArray;
32 
33 import com.android.internal.annotations.VisibleForTesting;
34 import com.android.internal.telephony.PhoneConfigurationManager;
35 import com.android.internal.util.IndentingPrintWriter;
36 
37 import java.io.FileDescriptor;
38 import java.io.PrintWriter;
39 
40 /**
41  * Singleton service setup to manage RCS related services that the platform provides such as User
42  * Capability Exchange.
43  */
44 @AnyThread
45 public class TelephonyRcsService {
46 
47     private static final String LOG_TAG = "TelephonyRcsService";
48 
49     /**
50      * Used to inject RcsFeatureController and UserCapabilityExchangeImpl instances for testing.
51      */
52     @VisibleForTesting
53     public interface FeatureFactory {
54         /**
55          * @return an {@link RcsFeatureController} assoicated with the slot specified.
56          */
createController(Context context, int slotId)57         RcsFeatureController createController(Context context, int slotId);
58 
59         /**
60          * @return an instance of {@link UserCapabilityExchangeImpl} associated with the slot
61          * specified.
62          */
createUserCapabilityExchange(Context context, int slotId, int subId)63         UserCapabilityExchangeImpl createUserCapabilityExchange(Context context, int slotId,
64                 int subId);
65     }
66 
67     private FeatureFactory mFeatureFactory = new FeatureFactory() {
68         @Override
69         public RcsFeatureController createController(Context context, int slotId) {
70             return new RcsFeatureController(context, slotId);
71         }
72 
73         @Override
74         public UserCapabilityExchangeImpl createUserCapabilityExchange(Context context, int slotId,
75                 int subId) {
76             return new UserCapabilityExchangeImpl(context, slotId, subId);
77         }
78     };
79 
80     // Notifies this service that there has been a change in available slots.
81     private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 1;
82 
83     private final Context mContext;
84     private final Object mLock = new Object();
85     private int mNumSlots;
86 
87     // Maps slot ID -> RcsFeatureController.
88     private SparseArray<RcsFeatureController> mFeatureControllers;
89 
90     private BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
91         @Override
92         public void onReceive(Context context, Intent intent) {
93             if (intent == null) {
94                 return;
95             }
96             if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
97                 Bundle bundle = intent.getExtras();
98                 if (bundle == null) {
99                     return;
100                 }
101                 int slotId = bundle.getInt(CarrierConfigManager.EXTRA_SLOT_INDEX,
102                         SubscriptionManager.INVALID_PHONE_INDEX);
103                 int subId = bundle.getInt(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
104                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
105                 updateFeatureControllerSubscription(slotId, subId);
106             }
107         }
108     };
109 
110     private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
111         switch (msg.what) {
112             case HANDLER_MSIM_CONFIGURATION_CHANGE: {
113                 AsyncResult result = (AsyncResult) msg.obj;
114                 Integer numSlots = (Integer) result.result;
115                 if (numSlots == null) {
116                     Log.w(LOG_TAG, "msim config change with null num slots.");
117                     break;
118                 }
119                 updateFeatureControllerSize(numSlots);
120                 break;
121             }
122             default:
123                 return false;
124         }
125         return true;
126     });
127 
TelephonyRcsService(Context context, int numSlots)128     public TelephonyRcsService(Context context, int numSlots) {
129         mContext = context;
130         mNumSlots = numSlots;
131         mFeatureControllers = new SparseArray<>(numSlots);
132     }
133 
134     /**
135      * @return the {@link RcsFeatureController} associated with the given slot.
136      */
getFeatureController(int slotId)137     public RcsFeatureController getFeatureController(int slotId) {
138         synchronized (mLock) {
139             return mFeatureControllers.get(slotId);
140         }
141     }
142 
143     /**
144      * Called after instance creation to initialize internal structures as well as register for
145      * system callbacks.
146      */
initialize()147     public void initialize() {
148         updateFeatureControllerSize(mNumSlots);
149 
150         PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
151                 HANDLER_MSIM_CONFIGURATION_CHANGE, null);
152         mContext.registerReceiver(mCarrierConfigChangedReceiver, new IntentFilter(
153                 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
154     }
155 
156     @VisibleForTesting
setFeatureFactory(FeatureFactory f)157     public void setFeatureFactory(FeatureFactory f) {
158         mFeatureFactory = f;
159     }
160 
161     /**
162      * Update the number of {@link RcsFeatureController}s that are created based on the number of
163      * active slots on the device.
164      */
165     @VisibleForTesting
updateFeatureControllerSize(int newNumSlots)166     public void updateFeatureControllerSize(int newNumSlots) {
167         synchronized (mLock) {
168             int oldNumSlots = mFeatureControllers.size();
169             if (oldNumSlots == newNumSlots) {
170                 return;
171             }
172             Log.i(LOG_TAG, "updateFeatureControllers: oldSlots=" + oldNumSlots + ", newNumSlots="
173                     + newNumSlots);
174             mNumSlots = newNumSlots;
175             if (oldNumSlots < newNumSlots) {
176                 for (int i = oldNumSlots; i < newNumSlots; i++) {
177                     RcsFeatureController c = constructFeatureController(i);
178                     // Do not add feature controllers for inactive subscriptions
179                     if (c.hasActiveFeatures()) {
180                         mFeatureControllers.put(i, c);
181                     }
182                 }
183             } else {
184                 for (int i = (oldNumSlots - 1); i > (newNumSlots - 1); i--) {
185                     RcsFeatureController c = mFeatureControllers.get(i);
186                     if (c != null) {
187                         mFeatureControllers.remove(i);
188                         c.destroy();
189                     }
190                 }
191             }
192         }
193     }
194 
updateFeatureControllerSubscription(int slotId, int newSubId)195     private void updateFeatureControllerSubscription(int slotId, int newSubId) {
196         synchronized (mLock) {
197             RcsFeatureController f = mFeatureControllers.get(slotId);
198             Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId + " newSubId="
199                     + newSubId + ", existing feature=" + (f != null));
200             if (SubscriptionManager.isValidSubscriptionId(newSubId)) {
201                 if (f == null) {
202                     // A controller doesn't exist for this slot yet.
203                     f = mFeatureFactory.createController(mContext, slotId);
204                     updateSupportedFeatures(f, slotId, newSubId);
205                     if (f.hasActiveFeatures()) mFeatureControllers.put(slotId, f);
206                 } else {
207                     updateSupportedFeatures(f, slotId, newSubId);
208                     // Do not keep an empty container around.
209                     if (!f.hasActiveFeatures()) {
210                         f.destroy();
211                         mFeatureControllers.remove(slotId);
212                     }
213                 }
214             }
215             if (f != null) f.updateAssociatedSubscription(newSubId);
216         }
217     }
218 
constructFeatureController(int slotId)219     private RcsFeatureController constructFeatureController(int slotId) {
220         RcsFeatureController c = mFeatureFactory.createController(mContext, slotId);
221         int subId = getSubscriptionFromSlot(slotId);
222         updateSupportedFeatures(c, slotId, subId);
223         return c;
224     }
225 
updateSupportedFeatures(RcsFeatureController c, int slotId, int subId)226     private void updateSupportedFeatures(RcsFeatureController c, int slotId, int subId) {
227         if (doesSubscriptionSupportPresence(subId)) {
228             if (c.getFeature(UserCapabilityExchangeImpl.class) == null) {
229                 c.addFeature(mFeatureFactory.createUserCapabilityExchange(mContext, slotId, subId),
230                         UserCapabilityExchangeImpl.class);
231             }
232         } else {
233             if (c.getFeature(UserCapabilityExchangeImpl.class) != null) {
234                 c.removeFeature(UserCapabilityExchangeImpl.class);
235             }
236         }
237         // Only start the connection procedure if we have active features.
238         if (c.hasActiveFeatures()) c.connect();
239     }
240 
doesSubscriptionSupportPresence(int subId)241     private boolean doesSubscriptionSupportPresence(int subId) {
242         if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
243         CarrierConfigManager carrierConfigManager =
244                 mContext.getSystemService(CarrierConfigManager.class);
245         if (carrierConfigManager == null) return false;
246         boolean supportsUce = carrierConfigManager.getConfigForSubId(subId).getBoolean(
247                 CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
248         supportsUce |= carrierConfigManager.getConfigForSubId(subId).getBoolean(
249                 CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL);
250         return supportsUce;
251     }
252 
253 
getSubscriptionFromSlot(int slotId)254     private int getSubscriptionFromSlot(int slotId) {
255         SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
256         if (manager == null) {
257             Log.w(LOG_TAG, "Couldn't find SubscriptionManager for slotId=" + slotId);
258             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
259         }
260         int[] subIds = manager.getSubscriptionIds(slotId);
261         if (subIds != null && subIds.length > 0) {
262             return subIds[0];
263         }
264         return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
265     }
266 
267     /**
268      * Dump this instance into a readable format for dumpsys usage.
269      */
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)270     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
271         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
272         pw.println("RcsFeatureControllers:");
273         pw.increaseIndent();
274         synchronized (mLock) {
275             for (int i = 0; i < mNumSlots; i++) {
276                 RcsFeatureController f = mFeatureControllers.get(i);
277                 if (f == null) continue;
278                 pw.increaseIndent();
279                 f.dump(fd, printWriter, args);
280                 pw.decreaseIndent();
281             }
282         }
283         pw.decreaseIndent();
284     }
285 }
286