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