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.telephony.metrics.RcsStats;
36 import com.android.internal.util.IndentingPrintWriter;
37 import com.android.phone.R;
38 
39 import java.io.FileDescriptor;
40 import java.io.PrintWriter;
41 
42 /**
43  * Singleton service setup to manage RCS related services that the platform provides such as User
44  * Capability Exchange.
45  */
46 @AnyThread
47 public class TelephonyRcsService {
48 
49     private static final String LOG_TAG = "TelephonyRcsService";
50 
51     /**
52      * Used to inject RcsFeatureController and UceController instances for testing.
53      */
54     @VisibleForTesting
55     public interface FeatureFactory {
56         /**
57          * @return an {@link RcsFeatureController} associated with the slot specified.
58          */
createController(Context context, int slotId, int subId)59         RcsFeatureController createController(Context context, int slotId, int subId);
60 
61         /**
62          * @return an instance of {@link UceControllerManager} associated with the slot specified.
63          */
createUceControllerManager(Context context, int slotId, int subId)64         UceControllerManager createUceControllerManager(Context context, int slotId, int subId);
65 
66         /**
67          * @return an instance of {@link SipTransportController} for the slot and subscription
68          * specified.
69          */
createSipTransportController(Context context, int slotId, int subId)70         SipTransportController createSipTransportController(Context context, int slotId, int subId);
71     }
72 
73     private FeatureFactory mFeatureFactory = new FeatureFactory() {
74         @Override
75         public RcsFeatureController createController(Context context, int slotId, int subId) {
76             return new RcsFeatureController(context, slotId, subId);
77         }
78 
79         @Override
80         public UceControllerManager createUceControllerManager(Context context, int slotId,
81                 int subId) {
82             return new UceControllerManager(context, slotId, subId);
83         }
84 
85         @Override
86         public SipTransportController createSipTransportController(Context context, int slotId,
87                 int subId) {
88             return new SipTransportController(context, slotId, subId);
89         }
90     };
91 
92     /**
93      * Used to inject device resource for testing.
94      */
95     @VisibleForTesting
96     public interface ResourceProxy {
97         /**
98          * @return an whether the device supports User Capability Exchange.
99          */
getDeviceUceEnabled(Context context)100         boolean getDeviceUceEnabled(Context context);
101     }
102 
103     private static ResourceProxy sResourceProxy = context -> {
104         return context.getResources().getBoolean(
105                 R.bool.config_rcs_user_capability_exchange_enabled);
106     };
107 
108     // Notifies this service that there has been a change in available slots.
109     private static final int HANDLER_MSIM_CONFIGURATION_CHANGE = 1;
110 
111     private final Context mContext;
112     private final Object mLock = new Object();
113     private int mNumSlots;
114 
115     // Maps slot ID -> RcsFeatureController.
116     private SparseArray<RcsFeatureController> mFeatureControllers;
117     // Maps slotId -> associatedSubIds
118     private SparseArray<Integer> mSlotToAssociatedSubIds;
119 
120     // Whether the device supports User Capability Exchange
121     private boolean mRcsUceEnabled;
122 
123     private BroadcastReceiver mCarrierConfigChangedReceiver = new BroadcastReceiver() {
124         @Override
125         public void onReceive(Context context, Intent intent) {
126             if (intent == null) {
127                 return;
128             }
129             if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
130                 Bundle bundle = intent.getExtras();
131                 if (bundle == null) {
132                     return;
133                 }
134                 int slotId = bundle.getInt(CarrierConfigManager.EXTRA_SLOT_INDEX,
135                         SubscriptionManager.INVALID_PHONE_INDEX);
136                 int subId = bundle.getInt(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
137                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
138                 onCarrierConfigChangedForSlot(slotId, subId);
139             }
140         }
141     };
142 
143     private Handler mHandler = new Handler(Looper.getMainLooper(), (msg) -> {
144         switch (msg.what) {
145             case HANDLER_MSIM_CONFIGURATION_CHANGE: {
146                 AsyncResult result = (AsyncResult) msg.obj;
147                 Integer numSlots = (Integer) result.result;
148                 if (numSlots == null) {
149                     Log.w(LOG_TAG, "msim config change with null num slots.");
150                     break;
151                 }
152                 updateFeatureControllerSize(numSlots);
153                 break;
154             }
155             default:
156                 return false;
157         }
158         return true;
159     });
160 
TelephonyRcsService(Context context, int numSlots)161     public TelephonyRcsService(Context context, int numSlots) {
162         mContext = context;
163         mNumSlots = numSlots;
164         mFeatureControllers = new SparseArray<>(numSlots);
165         mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
166         mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
167         RcsStats.getInstance().registerUceCallback();
168     }
169 
170     @VisibleForTesting
TelephonyRcsService(Context context, int numSlots, ResourceProxy resourceProxy)171     public TelephonyRcsService(Context context, int numSlots, ResourceProxy resourceProxy) {
172         mContext = context;
173         mNumSlots = numSlots;
174         mFeatureControllers = new SparseArray<>(numSlots);
175         mSlotToAssociatedSubIds = new SparseArray<>(numSlots);
176         sResourceProxy = resourceProxy;
177         mRcsUceEnabled = sResourceProxy.getDeviceUceEnabled(mContext);
178         RcsStats.getInstance().registerUceCallback();
179     }
180 
181     /**
182      * @return the {@link RcsFeatureController} associated with the given slot.
183      */
getFeatureController(int slotId)184     public RcsFeatureController getFeatureController(int slotId) {
185         synchronized (mLock) {
186             return mFeatureControllers.get(slotId);
187         }
188     }
189 
190     /**
191      * Called after instance creation to initialize internal structures as well as register for
192      * system callbacks.
193      */
initialize()194     public void initialize() {
195         updateFeatureControllerSize(mNumSlots);
196 
197         PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
198                 HANDLER_MSIM_CONFIGURATION_CHANGE, null);
199         mContext.registerReceiver(mCarrierConfigChangedReceiver, new IntentFilter(
200                 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED));
201     }
202 
203     @VisibleForTesting
setFeatureFactory(FeatureFactory f)204     public void setFeatureFactory(FeatureFactory f) {
205         mFeatureFactory = f;
206     }
207 
208     /**
209      * Update the number of {@link RcsFeatureController}s that are created based on the number of
210      * active slots on the device.
211      */
212     @VisibleForTesting
updateFeatureControllerSize(int newNumSlots)213     public void updateFeatureControllerSize(int newNumSlots) {
214         synchronized (mLock) {
215             int oldNumSlots = mFeatureControllers.size();
216             if (oldNumSlots == newNumSlots) {
217                 return;
218             }
219             Log.i(LOG_TAG, "updateFeatureControllers: oldSlots=" + oldNumSlots + ", newNumSlots="
220                     + newNumSlots);
221             mNumSlots = newNumSlots;
222             if (oldNumSlots < newNumSlots) {
223                 for (int i = oldNumSlots; i < newNumSlots; i++) {
224                     RcsFeatureController c = constructFeatureController(i);
225                     // Do not add feature controllers for inactive subscriptions
226                     if (c.hasActiveFeatures()) {
227                         mFeatureControllers.put(i, c);
228                         // Do not change mSlotToAssociatedSubIds, it will be updated upon carrier
229                         // config change.
230                     }
231                 }
232             } else {
233                 for (int i = (oldNumSlots - 1); i > (newNumSlots - 1); i--) {
234                     RcsFeatureController c = mFeatureControllers.get(i);
235                     if (c != null) {
236                         mFeatureControllers.remove(i);
237                         mSlotToAssociatedSubIds.remove(i);
238                         c.destroy();
239                     }
240                 }
241             }
242         }
243     }
244 
245     /**
246      * ACTION_CARRIER_CONFIG_CHANGED was received by this service for a specific slot.
247      * @param slotId The slotId associated with the event.
248      * @param subId The subId associated with the event. May cause the subId associated with the
249      *              RcsFeatureController to change if the subscription itself has changed.
250      */
onCarrierConfigChangedForSlot(int slotId, int subId)251     private void onCarrierConfigChangedForSlot(int slotId, int subId) {
252         synchronized (mLock) {
253             RcsFeatureController f = mFeatureControllers.get(slotId);
254             final int oldSubId = mSlotToAssociatedSubIds.get(slotId,
255                     SubscriptionManager.INVALID_SUBSCRIPTION_ID);
256             mSlotToAssociatedSubIds.put(slotId, subId);
257             Log.i(LOG_TAG, "updateFeatureControllerSubscription: slotId=" + slotId
258                     + ", oldSubId= " + oldSubId + ", subId=" + subId + ", existing feature="
259                     + (f != null));
260             if (SubscriptionManager.isValidSubscriptionId(subId)) {
261                 if (f == null) {
262                     // A controller doesn't exist for this slot yet.
263                     f = mFeatureFactory.createController(mContext, slotId, subId);
264                     updateSupportedFeatures(f, slotId, subId);
265                     if (f.hasActiveFeatures()) mFeatureControllers.put(slotId, f);
266                 } else {
267                     updateSupportedFeatures(f, slotId, subId);
268                     // Do not keep an empty container around.
269                     if (!f.hasActiveFeatures()) {
270                         f.destroy();
271                         mFeatureControllers.remove(slotId);
272                     }
273                 }
274             }
275             if (f != null) {
276                 if (oldSubId == subId) {
277                     f.onCarrierConfigChangedForSubscription();
278                 } else {
279                     f.updateAssociatedSubscription(subId);
280                 }
281             }
282         }
283     }
284 
constructFeatureController(int slotId)285     private RcsFeatureController constructFeatureController(int slotId) {
286         int subId = getSubscriptionFromSlot(slotId);
287         RcsFeatureController c = mFeatureFactory.createController(mContext, slotId, subId);
288         updateSupportedFeatures(c, slotId, subId);
289         return c;
290     }
291 
updateSupportedFeatures(RcsFeatureController c, int slotId, int subId)292     private void updateSupportedFeatures(RcsFeatureController c, int slotId, int subId) {
293         if (isDeviceUceEnabled() && doesSubscriptionSupportPresence(subId)) {
294             if (c.getFeature(UceControllerManager.class) == null) {
295                 c.addFeature(mFeatureFactory.createUceControllerManager(mContext, slotId, subId),
296                         UceControllerManager.class);
297             }
298         } else {
299             if (c.getFeature(UceControllerManager.class) != null) {
300                 c.removeFeature(UceControllerManager.class);
301             }
302         }
303 
304         if (doesSubscriptionSupportSingleRegistration(subId)) {
305             if (c.getFeature(SipTransportController.class) == null) {
306                 c.addFeature(mFeatureFactory.createSipTransportController(mContext, slotId, subId),
307                         SipTransportController.class);
308             }
309         } else {
310             if (c.getFeature(SipTransportController.class) != null) {
311                 c.removeFeature(SipTransportController.class);
312             }
313         }
314         // Only start the connection procedure if we have active features.
315         if (c.hasActiveFeatures()) c.connect();
316     }
317 
318     /**
319      * Get whether the device supports RCS User Capability Exchange or not.
320      */
isDeviceUceEnabled()321     public boolean isDeviceUceEnabled() {
322         return mRcsUceEnabled;
323     }
324 
325     /**
326      * Set the device supports RCS User Capability Exchange.
327      */
setDeviceUceEnabled(boolean isEnabled)328     public void setDeviceUceEnabled(boolean isEnabled) {
329         mRcsUceEnabled = isEnabled;
330     }
331 
doesSubscriptionSupportPresence(int subId)332     private boolean doesSubscriptionSupportPresence(int subId) {
333         if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
334         CarrierConfigManager carrierConfigManager =
335                 mContext.getSystemService(CarrierConfigManager.class);
336         if (carrierConfigManager == null) return false;
337         boolean supportsUce = carrierConfigManager.getConfigForSubId(subId).getBoolean(
338                 CarrierConfigManager.Ims.KEY_ENABLE_PRESENCE_PUBLISH_BOOL);
339         supportsUce |= carrierConfigManager.getConfigForSubId(subId).getBoolean(
340                 CarrierConfigManager.KEY_USE_RCS_SIP_OPTIONS_BOOL);
341         return supportsUce;
342     }
343 
doesSubscriptionSupportSingleRegistration(int subId)344     private boolean doesSubscriptionSupportSingleRegistration(int subId) {
345         if (!SubscriptionManager.isValidSubscriptionId(subId)) return false;
346         CarrierConfigManager carrierConfigManager =
347                 mContext.getSystemService(CarrierConfigManager.class);
348         if (carrierConfigManager == null) return false;
349         return carrierConfigManager.getConfigForSubId(subId).getBoolean(
350                 CarrierConfigManager.Ims.KEY_IMS_SINGLE_REGISTRATION_REQUIRED_BOOL);
351     }
352 
getSubscriptionFromSlot(int slotId)353     private int getSubscriptionFromSlot(int slotId) {
354         SubscriptionManager manager = mContext.getSystemService(SubscriptionManager.class);
355         if (manager == null) {
356             Log.w(LOG_TAG, "Couldn't find SubscriptionManager for slotId=" + slotId);
357             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
358         }
359         int[] subIds = manager.getSubscriptionIds(slotId);
360         if (subIds != null && subIds.length > 0) {
361             return subIds[0];
362         }
363         return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
364     }
365 
366     /**
367      * Dump this instance into a readable format for dumpsys usage.
368      */
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)369     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
370         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
371         pw.println("RcsFeatureControllers:");
372         pw.increaseIndent();
373         synchronized (mLock) {
374             for (int i = 0; i < mNumSlots; i++) {
375                 RcsFeatureController f = mFeatureControllers.get(i);
376                 if (f == null) continue;
377                 pw.increaseIndent();
378                 f.dump(fd, printWriter, args);
379                 pw.decreaseIndent();
380             }
381         }
382         pw.decreaseIndent();
383     }
384 }
385