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