• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.internal.telephony.ims;
18 
19 import static android.telephony.SubscriptionManager.PLACEHOLDER_SUBSCRIPTION_ID_BASE;
20 
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.ServiceConnection;
25 import android.content.pm.ChangedPackages;
26 import android.content.pm.PackageManager;
27 import android.os.Handler;
28 import android.os.HandlerThread;
29 import android.os.IBinder;
30 import android.os.IInterface;
31 import android.os.RemoteException;
32 import android.os.UserHandle;
33 import android.permission.LegacyPermissionManager;
34 import android.telephony.AnomalyReporter;
35 import android.telephony.SubscriptionManager;
36 import android.telephony.ims.ImsService;
37 import android.telephony.ims.aidl.IImsConfig;
38 import android.telephony.ims.aidl.IImsRegistration;
39 import android.telephony.ims.aidl.IImsServiceController;
40 import android.telephony.ims.aidl.ISipTransport;
41 import android.telephony.ims.feature.ImsFeature;
42 import android.telephony.ims.stub.ImsFeatureConfiguration;
43 import android.util.LocalLog;
44 import android.util.Log;
45 import android.util.SparseIntArray;
46 
47 import com.android.ims.ImsFeatureBinderRepository;
48 import com.android.ims.ImsFeatureContainer;
49 import com.android.ims.internal.IImsFeatureStatusCallback;
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.internal.telephony.ExponentialBackoff;
52 import com.android.internal.telephony.util.TelephonyUtils;
53 
54 import java.io.PrintWriter;
55 import java.util.HashSet;
56 import java.util.List;
57 import java.util.Set;
58 import java.util.UUID;
59 import java.util.concurrent.CountDownLatch;
60 import java.util.stream.Collectors;
61 
62 /**
63  * Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the
64  * ImsService will support.
65  *
66  * When the ImsService is first bound, {@link ImsService#createMmTelFeature(int)} and
67  * {@link ImsService#createRcsFeature(int)} will be called
68  * on each feature that the service supports. For each ImsFeature that is created,
69  * {@link ImsServiceControllerCallbacks#imsServiceFeatureCreated} will be called to notify the
70  * listener that the ImsService now supports that feature.
71  *
72  * When {@link #changeImsServiceFeatures} is called with a set of features that is different from
73  * the original set, create*Feature and {@link IImsServiceController#removeImsFeature} will be
74  * called for each feature that is created/removed.
75  */
76 public class ImsServiceController {
77     private final UUID mAnomalyUUID = UUID.fromString("e93b05e4-6d0a-4755-a6da-a2d2dbfb10d6");
78     private int mLastSequenceNumber = 0;
79     private ChangedPackages mChangedPackages;
80     private PackageManager mPackageManager;
81     class ImsServiceConnection implements ServiceConnection {
82         // Track the status of whether or not the Service has died in case we need to permanently
83         // unbind (see onNullBinding below).
84         private boolean mIsServiceConnectionDead = false;
85 
86         @Override
onServiceConnected(ComponentName name, IBinder service)87         public void onServiceConnected(ComponentName name, IBinder service) {
88             synchronized (mLock) {
89                 mBackoff.stop();
90                 mIsBound = true;
91                 mIsBinding = false;
92                 try {
93                     mLocalLog.log("onServiceConnected");
94                     Log.d(LOG_TAG, "ImsService(" + name + "): onServiceConnected with binder: "
95                             + service);
96                     setServiceController(service);
97                     notifyImsServiceReady();
98                     retrieveStaticImsServiceCapabilities();
99                     // create all associated features in the ImsService
100                     for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
101                         long caps = modifyCapabiltiesForSlot(mImsFeatures, i.slotId,
102                                 mServiceCapabilities);
103                         addImsServiceFeature(i, caps, mSlotIdToSubIdMap.get(i.slotId));
104                     }
105                 } catch (RemoteException e) {
106                     mIsBound = false;
107                     mIsBinding = false;
108                     // RemoteException means that the process holding the binder died or something
109                     // unexpected happened... try a full rebind.
110                     cleanupConnection();
111                     unbindService();
112                     startDelayedRebindToService();
113                     mLocalLog.log("onConnected exception=" + e.getMessage() + ", retry in "
114                             + mBackoff.getCurrentDelay() + " mS");
115                     Log.e(LOG_TAG, "ImsService(" + name + ") RemoteException:"
116                             + e.getMessage());
117                 }
118             }
119         }
120 
121         @Override
onServiceDisconnected(ComponentName name)122         public void onServiceDisconnected(ComponentName name) {
123             synchronized (mLock) {
124                 mIsBinding = false;
125                 cleanupConnection();
126             }
127             mLocalLog.log("onServiceDisconnected");
128             Log.w(LOG_TAG, "ImsService(" + name + "): onServiceDisconnected. Waiting...");
129             // Service disconnected, but we are still technically bound. Waiting for reconnect.
130             checkAndReportAnomaly(name);
131         }
132 
133         @Override
onBindingDied(ComponentName name)134         public void onBindingDied(ComponentName name) {
135             mIsServiceConnectionDead = true;
136             synchronized (mLock) {
137                 mIsBinding = false;
138                 mIsBound = false;
139                 // according to the docs, we should fully unbind before rebinding again.
140                 cleanupConnection();
141                 unbindService();
142                 startDelayedRebindToService();
143             }
144             Log.w(LOG_TAG, "ImsService(" + name + "): onBindingDied. Starting rebind...");
145             mLocalLog.log("onBindingDied, retrying in " + mBackoff.getCurrentDelay() + " mS");
146         }
147 
148         @Override
onNullBinding(ComponentName name)149         public void onNullBinding(ComponentName name) {
150             Log.w(LOG_TAG, "ImsService(" + name + "): onNullBinding. Is service dead = "
151                     + mIsServiceConnectionDead);
152             mLocalLog.log("onNullBinding, is service dead = " + mIsServiceConnectionDead);
153             // onNullBinding will happen after onBindingDied. In this case, we should not
154             // permanently unbind and instead let the automatic rebind occur.
155             if (mIsServiceConnectionDead) return;
156             synchronized (mLock) {
157                 mIsBinding = false;
158                 // Service connection exists, so we are bound but the binder is null. Wait for
159                 // ImsResolver to trigger the unbind here.
160                 mIsBound = true;
161                 cleanupConnection();
162             }
163             if (mCallbacks != null) {
164                 // Will trigger an unbind.
165                 mCallbacks.imsServiceBindPermanentError(getComponentName());
166             }
167         }
168 
169         // Does not clear feature configuration, just cleans up the active callbacks and
170         // invalidates remote FeatureConnections.
171         // This should only be called when locked
cleanupConnection()172         private void cleanupConnection() {
173             cleanupAllFeatures();
174             setServiceController(null);
175         }
176     }
177 
178     /**
179      * Defines callbacks that are used by the ImsServiceController to notify when an ImsService
180      * has created or removed a new feature as well as the associated ImsServiceController.
181      */
182     public interface ImsServiceControllerCallbacks {
183         /**
184          * Called by ImsServiceController when a new MMTEL or RCS feature has been created.
185          */
imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller)186         void imsServiceFeatureCreated(int slotId, int feature, ImsServiceController controller);
187         /**
188          * Called by ImsServiceController when a new MMTEL or RCS feature has been removed.
189          */
imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller)190         void imsServiceFeatureRemoved(int slotId, int feature, ImsServiceController controller);
191 
192         /**
193          * Called by the ImsServiceController when the ImsService has notified the framework that
194          * its features have changed.
195          */
imsServiceFeaturesChanged(ImsFeatureConfiguration config, ImsServiceController controller)196         void imsServiceFeaturesChanged(ImsFeatureConfiguration config,
197                 ImsServiceController controller);
198 
199         /**
200          * Called by the ImsServiceController when there has been an error binding that is
201          * not recoverable, such as the ImsService returning a null binder.
202          */
imsServiceBindPermanentError(ComponentName name)203         void imsServiceBindPermanentError(ComponentName name);
204     }
205 
206     /**
207      * Returns the currently defined rebind retry timeout. Used for testing.
208      */
209     @VisibleForTesting
210     public interface RebindRetry {
211         /**
212          * Returns a long in ms indicating how long the ImsServiceController should wait before
213          * rebinding for the first time.
214          */
getStartDelay()215         long getStartDelay();
216 
217         /**
218          * Returns a long in ms indicating the maximum time the ImsServiceController should wait
219          * before rebinding.
220          */
getMaximumDelay()221         long getMaximumDelay();
222     }
223 
224     private static final String LOG_TAG = "ImsServiceController";
225     private static final int REBIND_START_DELAY_MS = 2 * 1000; // 2 seconds
226     private static final int REBIND_MAXIMUM_DELAY_MS = 60 * 1000; // 1 minute
227     private static final long CHANGE_PERMISSION_TIMEOUT_MS = 15 * 1000; // 15 seconds
228     // Enforce ImsService has both MMTEL and RCS supported in order to enable SIP transport API.
229     // Enable ImsServiceControllerTest and SipDelegateManagerTest cases if this is re-enabled.
230     private static final boolean ENFORCE_SINGLE_SERVICE_FOR_SIP_TRANSPORT = false;
231     private final ComponentName mComponentName;
232     private final HandlerThread mHandlerThread = new HandlerThread("ImsServiceControllerHandler");
233     private final LegacyPermissionManager mPermissionManager;
234     private ImsFeatureBinderRepository mRepo;
235     private ImsServiceControllerCallbacks mCallbacks;
236     private ExponentialBackoff mBackoff;
237 
238     private boolean mIsBound = false;
239     private boolean mIsBinding = false;
240     // Set of a pair of slotId->feature
241     private Set<ImsFeatureConfiguration.FeatureSlotPair> mImsFeatures;
242     private SparseIntArray mSlotIdToSubIdMap;
243     private IImsServiceController mIImsServiceController;
244     // The Capabilities bitmask of the connected ImsService (see ImsService#ImsServiceCapability).
245     private long mServiceCapabilities;
246     private ImsServiceConnection mImsServiceConnection;
247     // Only added or removed, never accessed on purpose.
248     private Set<ImsFeatureStatusCallback> mFeatureStatusCallbacks = new HashSet<>();
249     private final LocalLog mLocalLog = new LocalLog(8);
250 
251     protected final Object mLock = new Object();
252     protected final Context mContext;
253 
254     private ImsService.Listener mFeatureChangedListener = new ImsService.Listener() {
255         @Override
256         public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
257             if (mCallbacks == null) {
258                 return;
259             }
260             mLocalLog.log("onUpdateSupportedImsFeatures to " + c.getServiceFeatures());
261             mCallbacks.imsServiceFeaturesChanged(c, ImsServiceController.this);
262         }
263     };
264 
265     /**
266      * Container class for the IImsFeatureStatusCallback callback implementation. This class is
267      * never used directly, but we need to keep track of the IImsFeatureStatusCallback
268      * implementations explicitly.
269      */
270     private class ImsFeatureStatusCallback {
271         private int mSlotId;
272         private int mFeatureType;
273 
274         private final IImsFeatureStatusCallback mCallback = new IImsFeatureStatusCallback.Stub() {
275 
276             @Override
277             public void notifyImsFeatureStatus(int featureStatus) throws RemoteException {
278                 Log.i(LOG_TAG, "notifyImsFeatureStatus: slot=" + mSlotId + ", feature="
279                         + ImsFeature.FEATURE_LOG_MAP.get(mFeatureType) + ", status="
280                         + ImsFeature.STATE_LOG_MAP.get(featureStatus));
281                 mRepo.notifyFeatureStateChanged(mSlotId, mFeatureType, featureStatus);
282             }
283         };
284 
ImsFeatureStatusCallback(int slotId, int featureType)285         ImsFeatureStatusCallback(int slotId, int featureType) {
286             mSlotId = slotId;
287             mFeatureType = featureType;
288         }
289 
getCallback()290         public IImsFeatureStatusCallback getCallback() {
291             return mCallback;
292         }
293     }
294 
295     // Retry the bind to the ImsService that has died after mRebindRetry timeout.
296     private Runnable mRestartImsServiceRunnable = new Runnable() {
297         @Override
298         public void run() {
299             synchronized (mLock) {
300                 if (mIsBound) {
301                     return;
302                 }
303                 bind(mImsFeatures, mSlotIdToSubIdMap);
304             }
305         }
306     };
307 
308     private RebindRetry mRebindRetry = new RebindRetry() {
309         @Override
310         public long getStartDelay() {
311             return REBIND_START_DELAY_MS;
312         }
313 
314         @Override
315         public long getMaximumDelay() {
316             return REBIND_MAXIMUM_DELAY_MS;
317         }
318     };
319 
ImsServiceController(Context context, ComponentName componentName, ImsServiceControllerCallbacks callbacks, ImsFeatureBinderRepository repo)320     public ImsServiceController(Context context, ComponentName componentName,
321             ImsServiceControllerCallbacks callbacks, ImsFeatureBinderRepository repo) {
322         mContext = context;
323         mComponentName = componentName;
324         mCallbacks = callbacks;
325         mHandlerThread.start();
326         mBackoff = new ExponentialBackoff(
327                 mRebindRetry.getStartDelay(),
328                 mRebindRetry.getMaximumDelay(),
329                 2, /* multiplier */
330                 mHandlerThread.getLooper(),
331                 mRestartImsServiceRunnable);
332         mPermissionManager = (LegacyPermissionManager) mContext.getSystemService(
333                 Context.LEGACY_PERMISSION_SERVICE);
334         mRepo = repo;
335 
336         mPackageManager = mContext.getPackageManager();
337         if (mPackageManager != null) {
338             mChangedPackages = mPackageManager.getChangedPackages(mLastSequenceNumber);
339             if (mChangedPackages != null) {
340                 mLastSequenceNumber = mChangedPackages.getSequenceNumber();
341             }
342         }
343     }
344 
345     @VisibleForTesting
346     // Creating a new HandlerThread and background handler for each test causes a segfault, so for
347     // testing, use a handler supplied by the testing system.
ImsServiceController(Context context, ComponentName componentName, ImsServiceControllerCallbacks callbacks, Handler handler, RebindRetry rebindRetry, ImsFeatureBinderRepository repo)348     public ImsServiceController(Context context, ComponentName componentName,
349             ImsServiceControllerCallbacks callbacks, Handler handler, RebindRetry rebindRetry,
350             ImsFeatureBinderRepository repo) {
351         mContext = context;
352         mComponentName = componentName;
353         mCallbacks = callbacks;
354         mBackoff = new ExponentialBackoff(
355                 rebindRetry.getStartDelay(),
356                 rebindRetry.getMaximumDelay(),
357                 2, /* multiplier */
358                 handler,
359                 mRestartImsServiceRunnable);
360         mPermissionManager = null;
361         mRepo = repo;
362     }
363 
364     /**
365      * Sends request to bind to ImsService designated by the {@link ComponentName} with the feature
366      * set imsFeatureSet.
367      *
368      * @param imsFeatureSet a Set of Pairs that designate the slotId->featureId that need to be
369      *                      created once the service is bound.
370      * @return {@link true} if the service is in the process of being bound, {@link false} if it
371      * has failed.
372      */
bind(Set<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet, SparseIntArray slotIdToSubIdMap)373     public boolean bind(Set<ImsFeatureConfiguration.FeatureSlotPair> imsFeatureSet,
374             SparseIntArray  slotIdToSubIdMap) {
375         synchronized (mLock) {
376             if (!mIsBound && !mIsBinding) {
377                 mIsBinding = true;
378                 sanitizeFeatureConfig(imsFeatureSet);
379                 mImsFeatures = imsFeatureSet;
380                 mSlotIdToSubIdMap = slotIdToSubIdMap;
381                 grantPermissionsToService();
382                 Intent imsServiceIntent = new Intent(getServiceInterface()).setComponent(
383                         mComponentName);
384                 mImsServiceConnection = new ImsServiceConnection();
385                 int serviceFlags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
386                         | Context.BIND_IMPORTANT;
387                 mLocalLog.log("binding " + imsFeatureSet);
388                 Log.i(LOG_TAG, "Binding ImsService:" + mComponentName);
389                 try {
390                     boolean bindSucceeded = mContext.bindService(imsServiceIntent,
391                             mImsServiceConnection, serviceFlags);
392                     if (!bindSucceeded) {
393                         mLocalLog.log("    binding failed, retrying in "
394                                 + mBackoff.getCurrentDelay() + " mS");
395                         mIsBinding = false;
396                         mBackoff.notifyFailed();
397                     }
398                     return bindSucceeded;
399                 } catch (Exception e) {
400                     mBackoff.notifyFailed();
401                     mLocalLog.log("    binding exception=" + e.getMessage() + ", retrying in "
402                             + mBackoff.getCurrentDelay() + " mS");
403                     Log.e(LOG_TAG, "Error binding (" + mComponentName + ") with exception: "
404                             + e.getMessage() + ", rebinding in " + mBackoff.getCurrentDelay()
405                             + " ms");
406                     return false;
407                 }
408             } else {
409                 return false;
410             }
411         }
412     }
413 
414     /**
415      * Ensure the feature includes MMTEL when it supports EMERGENCY_MMTEL, if not, remove.
416      */
sanitizeFeatureConfig(Set<ImsFeatureConfiguration.FeatureSlotPair> features)417     private void sanitizeFeatureConfig(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
418         Set<ImsFeatureConfiguration.FeatureSlotPair> emergencyMmtelFeatures = features.stream()
419                 .filter(feature -> feature.featureType == ImsFeature.FEATURE_EMERGENCY_MMTEL)
420                 .collect(Collectors.toSet());
421         for (ImsFeatureConfiguration.FeatureSlotPair feature : emergencyMmtelFeatures) {
422             if (!features.contains(new ImsFeatureConfiguration.FeatureSlotPair(feature.slotId,
423                     ImsFeature.FEATURE_MMTEL))) {
424                 features.remove(feature);
425             }
426         }
427     }
428 
429     /**
430      * Calls {@link IImsServiceController#removeImsFeature} on all features that the
431      * ImsService supports and then unbinds the service.
432      */
unbind()433     public void unbind() throws RemoteException {
434         synchronized (mLock) {
435             mBackoff.stop();
436             // Clean up all features
437             changeImsServiceFeatures(new HashSet<>(), mSlotIdToSubIdMap);
438             mIsBound = false;
439             mIsBinding = false;
440             setServiceController(null);
441             unbindService();
442         }
443     }
444 
445     /**
446      * For every feature that is added, the service calls the associated create. For every
447      * ImsFeature that is removed, {@link IImsServiceController#removeImsFeature} is called.
448      */
changeImsServiceFeatures( Set<ImsFeatureConfiguration.FeatureSlotPair> newImsFeatures, SparseIntArray slotIdToSubIdMap)449     public void changeImsServiceFeatures(
450             Set<ImsFeatureConfiguration.FeatureSlotPair> newImsFeatures,
451                     SparseIntArray  slotIdToSubIdMap) throws RemoteException {
452         sanitizeFeatureConfig(newImsFeatures);
453         synchronized (mLock) {
454             HashSet<Integer> slotIDs = newImsFeatures.stream().map(e -> e.slotId).collect(
455                     Collectors.toCollection(HashSet::new));
456             // detect which subIds have changed on a per-slot basis
457             SparseIntArray changedSubIds = new SparseIntArray(slotIDs.size());
458             for (Integer slotID : slotIDs) {
459                 int oldSubId = mSlotIdToSubIdMap.get(slotID, PLACEHOLDER_SUBSCRIPTION_ID_BASE);
460                 int newSubId = slotIdToSubIdMap.get(slotID);
461                 if (oldSubId != newSubId) {
462                     changedSubIds.put(slotID, newSubId);
463                     mLocalLog.log("subId changed for slot: " + slotID + ", " + oldSubId + " -> "
464                             + newSubId);
465                     Log.i(LOG_TAG, "subId changed for slot: " + slotID + ", " + oldSubId + " -> "
466                             + newSubId);
467                 }
468             }
469             mSlotIdToSubIdMap = slotIdToSubIdMap;
470             // no change, return early.
471             if (mImsFeatures.equals(newImsFeatures) && changedSubIds.size() == 0) {
472                 return;
473             }
474             mLocalLog.log("Features (" + mImsFeatures + "->" + newImsFeatures + ")");
475             Log.i(LOG_TAG, "Features (" + mImsFeatures + "->" + newImsFeatures + ") for "
476                     + "ImsService: " + mComponentName);
477             HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldImsFeatures =
478                     new HashSet<>(mImsFeatures);
479             // Set features first in case we lose binding and need to rebind later.
480             mImsFeatures = newImsFeatures;
481             if (mIsBound) {
482                 // add features to service.
483                 HashSet<ImsFeatureConfiguration.FeatureSlotPair> newFeatures =
484                         new HashSet<>(mImsFeatures);
485                 newFeatures.removeAll(oldImsFeatures);
486                 for (ImsFeatureConfiguration.FeatureSlotPair i : newFeatures) {
487                     long caps = modifyCapabiltiesForSlot(mImsFeatures, i.slotId,
488                             mServiceCapabilities);
489                     addImsServiceFeature(i, caps, mSlotIdToSubIdMap.get(i.slotId));
490                 }
491                 // remove old features
492                 HashSet<ImsFeatureConfiguration.FeatureSlotPair> oldFeatures =
493                         new HashSet<>(oldImsFeatures);
494                 oldFeatures.removeAll(mImsFeatures);
495                 for (ImsFeatureConfiguration.FeatureSlotPair i : oldFeatures) {
496                     removeImsServiceFeature(i, false);
497                 }
498                 // ensure the capabilities have been updated for unchanged features.
499                 HashSet<ImsFeatureConfiguration.FeatureSlotPair> unchangedFeatures =
500                         new HashSet<>(mImsFeatures);
501                 unchangedFeatures.removeAll(oldFeatures);
502                 unchangedFeatures.removeAll(newFeatures);
503                 // Go through ImsFeatures whose associated subId have changed and recreate them.
504                 if (changedSubIds.size() > 0) {
505                     for (int slotId : changedSubIds.copyKeys()) {
506                         int subId = changedSubIds.get(slotId,
507                                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
508                         HashSet<ImsFeatureConfiguration.FeatureSlotPair>
509                                 removeAddFeatures = unchangedFeatures.stream()
510                                 .filter(e -> e.slotId == slotId).collect(
511                                         Collectors.toCollection(HashSet::new));
512                         for (ImsFeatureConfiguration.FeatureSlotPair i : removeAddFeatures) {
513                             removeImsServiceFeature(i, true);
514                         }
515                         for (ImsFeatureConfiguration.FeatureSlotPair i : removeAddFeatures) {
516                             long caps = modifyCapabiltiesForSlot(mImsFeatures, i.slotId,
517                                     mServiceCapabilities);
518                             addImsServiceFeature(i, caps, subId);
519                         }
520                         unchangedFeatures.removeAll(removeAddFeatures);
521                     }
522                 }
523                 for (ImsFeatureConfiguration.FeatureSlotPair p : unchangedFeatures) {
524                     long caps = modifyCapabiltiesForSlot(mImsFeatures, p.slotId,
525                             mServiceCapabilities);
526                     mRepo.notifyFeatureCapabilitiesChanged(p.slotId, p.featureType, caps);
527                 }
528             }
529         }
530     }
531 
532     @VisibleForTesting
getImsServiceController()533     public IImsServiceController getImsServiceController() {
534         return mIImsServiceController;
535     }
536 
537     @VisibleForTesting
getRebindDelay()538     public long getRebindDelay() {
539         return mBackoff.getCurrentDelay();
540     }
541 
542     @VisibleForTesting
stopBackoffTimerForTesting()543     public void stopBackoffTimerForTesting() {
544         mBackoff.stop();
545     }
546 
getComponentName()547     public ComponentName getComponentName() {
548         return mComponentName;
549     }
550 
551     /**
552      * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and
553      * trigger ImsFeature status updates.
554      */
enableIms(int slotId, int subId)555     public void enableIms(int slotId, int subId) {
556         try {
557             synchronized (mLock) {
558                 if (isServiceControllerAvailable()) {
559                     mIImsServiceController.enableIms(slotId, subId);
560                 }
561             }
562         } catch (RemoteException e) {
563             Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage());
564         }
565     }
566 
567     /**
568      * Notify ImsService to disable IMS for the framework. This will trigger IMS de-registration and
569      * trigger ImsFeature capability status to become false.
570      */
disableIms(int slotId, int subId)571     public void disableIms(int slotId, int subId) {
572         try {
573             synchronized (mLock) {
574                 if (isServiceControllerAvailable()) {
575                     mIImsServiceController.disableIms(slotId, subId);
576                 }
577             }
578         } catch (RemoteException e) {
579             Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage());
580         }
581     }
582 
583     /**
584      * @return the IImsRegistration that corresponds to the slot id specified.
585      */
getRegistration(int slotId, int subId)586     public IImsRegistration getRegistration(int slotId, int subId) throws RemoteException {
587         synchronized (mLock) {
588             return isServiceControllerAvailable()
589                     ? mIImsServiceController.getRegistration(slotId, subId) : null;
590         }
591     }
592 
593     /**
594      * @return the IImsConfig that corresponds to the slot id specified.
595      */
getConfig(int slotId, int subId)596     public IImsConfig getConfig(int slotId, int subId) throws RemoteException {
597         synchronized (mLock) {
598             return isServiceControllerAvailable()
599                     ? mIImsServiceController.getConfig(slotId, subId) : null;
600         }
601     }
602 
603     /**
604      * @return the ISipTransport instance associated with the requested slot ID.
605      */
getSipTransport(int slotId)606     public ISipTransport getSipTransport(int slotId) throws RemoteException {
607         synchronized (mLock) {
608             return isServiceControllerAvailable()
609                     ? mIImsServiceController.getSipTransport(slotId) : null;
610         }
611     }
612 
getStaticServiceCapabilities()613     protected long getStaticServiceCapabilities() throws RemoteException {
614         synchronized (mLock) {
615             return isServiceControllerAvailable()
616                     ? mIImsServiceController.getImsServiceCapabilities() : 0L;
617         }
618     }
619 
620     /**
621      * notify the ImsService that the ImsService is ready for feature creation.
622      */
notifyImsServiceReady()623     protected void notifyImsServiceReady() throws RemoteException {
624         synchronized (mLock) {
625             if (isServiceControllerAvailable()) {
626                 Log.d(LOG_TAG, "notifyImsServiceReady");
627                 mIImsServiceController.setListener(mFeatureChangedListener);
628                 mIImsServiceController.notifyImsServiceReadyForFeatureCreation();
629             }
630         }
631     }
632 
retrieveStaticImsServiceCapabilities()633     private void retrieveStaticImsServiceCapabilities() throws RemoteException {
634         long caps = getStaticServiceCapabilities();
635         Log.i(LOG_TAG, "retrieveStaticImsServiceCapabilities: "
636                 + ImsService.getCapabilitiesString(caps));
637         mLocalLog.log("retrieveStaticImsServiceCapabilities: "
638                 + ImsService.getCapabilitiesString(caps));
639         synchronized (mLock) {
640             mServiceCapabilities = caps;
641         }
642     }
643 
getServiceInterface()644     protected String getServiceInterface() {
645         return ImsService.SERVICE_INTERFACE;
646     }
647 
648     /**
649      * Sets the IImsServiceController instance. Overridden by compat layers to set compatibility
650      * versions of this service controller.
651      */
setServiceController(IBinder serviceController)652     protected void setServiceController(IBinder serviceController) {
653         mIImsServiceController = IImsServiceController.Stub.asInterface(serviceController);
654     }
655 
656     /**
657      * Check to see if the service controller is available, overridden for compat versions,
658      * @return true if available, false otherwise;
659      */
isServiceControllerAvailable()660     protected boolean isServiceControllerAvailable() {
661         return mIImsServiceController != null;
662     }
663 
664     // Only add a new rebind if there are no pending rebinds waiting.
startDelayedRebindToService()665     private void startDelayedRebindToService() {
666         mBackoff.start();
667     }
668 
unbindService()669     private void unbindService() {
670         synchronized (mLock) {
671             if (mImsServiceConnection != null) {
672                 Log.i(LOG_TAG, "Unbinding ImsService: " + mComponentName);
673                 mLocalLog.log("unbinding: " + mComponentName);
674                 mContext.unbindService(mImsServiceConnection);
675                 mImsServiceConnection = null;
676             } else {
677                 Log.i(LOG_TAG, "unbindService called on already unbound ImsService: "
678                         + mComponentName);
679                 mLocalLog.log("Note: unbindService called with no ServiceConnection on "
680                         + mComponentName);
681             }
682         }
683     }
684 
685     /**
686      * Modify the capabilities returned by the ImsService based on the state of this controller:
687      * - CAPABILITY_EMERGENCY_OVER_MMTEL should only be set if features contains
688      * FEATURE_EMERGENCY_MMTEL (This is not set by the ImsService itself).
689      * - CAPABILITY_SIP_DELEGATE_CREATION should only be set in the case that this ImsService is
690      * handling both MMTEL and RCS features for this slot.
691      */
modifyCapabiltiesForSlot( Set<ImsFeatureConfiguration.FeatureSlotPair> features, int slotId, long serviceCaps)692     private long modifyCapabiltiesForSlot(
693             Set<ImsFeatureConfiguration.FeatureSlotPair> features, int slotId, long serviceCaps) {
694         long caps = serviceCaps;
695         List<Integer> featureTypes = getFeaturesForSlot(slotId, features);
696         if (featureTypes.contains(ImsFeature.FEATURE_EMERGENCY_MMTEL)) {
697             // We only consider MMTEL_EMERGENCY as a capability here, so set the capability if
698             // the ImsService has declared it.
699             caps |= ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL;
700         }
701 
702         if (ENFORCE_SINGLE_SERVICE_FOR_SIP_TRANSPORT) {
703             if (!featureTypes.contains(ImsFeature.FEATURE_MMTEL)
704                     || !featureTypes.contains(ImsFeature.FEATURE_RCS)) {
705                 // Only allow SipDelegate creation if this ImsService is providing both MMTEL and
706                 // RCS features.
707                 caps &= ~(ImsService.CAPABILITY_SIP_DELEGATE_CREATION);
708             }
709         } else {
710             Log.i(LOG_TAG, "skipping single service enforce check...");
711         }
712         return caps;
713     }
714 
715     // Grant runtime permissions to ImsService. PermissionManager ensures that the ImsService is
716     // system/signed before granting permissions.
grantPermissionsToService()717     private void grantPermissionsToService() {
718         mLocalLog.log("grant permissions to " + getComponentName());
719         Log.i(LOG_TAG, "Granting Runtime permissions to:" + getComponentName());
720         String[] pkgToGrant = {mComponentName.getPackageName()};
721         try {
722             if (mPermissionManager != null) {
723                 CountDownLatch latch = new CountDownLatch(1);
724                 mPermissionManager.grantDefaultPermissionsToEnabledImsServices(
725                         pkgToGrant, UserHandle.of(UserHandle.myUserId()), Runnable::run,
726                         isSuccess -> {
727                             if (isSuccess) {
728                                 latch.countDown();
729                             } else {
730                                 Log.e(LOG_TAG, "Failed to grant permissions to service.");
731                             }
732                         });
733                 TelephonyUtils.waitUntilReady(latch, CHANGE_PERMISSION_TIMEOUT_MS);
734             }
735         } catch (RuntimeException e) {
736             Log.w(LOG_TAG, "Unable to grant permissions, binder died.");
737         }
738     }
739 
740     // This method should only be called when synchronized on mLock
addImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair, long capabilities, int subId)741     private void addImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair,
742             long capabilities, int subId) throws RemoteException {
743         if (!isServiceControllerAvailable() || mCallbacks == null) {
744             Log.w(LOG_TAG, "addImsServiceFeature called with null values.");
745             return;
746         }
747         if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
748             IInterface f = createImsFeature(
749                     featurePair.slotId, subId, featurePair.featureType, capabilities);
750             addImsFeatureBinder(featurePair.slotId, subId, featurePair.featureType,
751                     f, capabilities);
752             addImsFeatureStatusCallback(featurePair.slotId, featurePair.featureType);
753         } else {
754             // Don't update ImsService for emergency MMTEL feature.
755             Log.i(LOG_TAG, "supports emergency calling on slot " + featurePair.slotId);
756         }
757         // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
758         mCallbacks.imsServiceFeatureCreated(featurePair.slotId, featurePair.featureType, this);
759     }
760 
761     // This method should only be called when synchronized on mLock
removeImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair, boolean changeSubId)762     private void removeImsServiceFeature(ImsFeatureConfiguration.FeatureSlotPair featurePair,
763             boolean changeSubId) {
764         if (!isServiceControllerAvailable() || mCallbacks == null) {
765             Log.w(LOG_TAG, "removeImsServiceFeature called with null values.");
766             return;
767         }
768         // Signal ImsResolver to change supported ImsFeatures for this ImsServiceController
769         mCallbacks.imsServiceFeatureRemoved(featurePair.slotId, featurePair.featureType, this);
770         if (featurePair.featureType != ImsFeature.FEATURE_EMERGENCY_MMTEL) {
771             removeImsFeatureStatusCallback(featurePair.slotId, featurePair.featureType);
772             removeImsFeatureBinder(featurePair.slotId, featurePair.featureType);
773             try {
774                 removeImsFeature(featurePair.slotId, featurePair.featureType, changeSubId);
775             } catch (RemoteException e) {
776                 // The connection to this ImsService doesn't exist. This may happen if the service
777                 // has died and we are removing features.
778                 Log.i(LOG_TAG, "Couldn't remove feature {"
779                         + ImsFeature.FEATURE_LOG_MAP.get(featurePair.featureType)
780                         + "}, connection is down: " + e.getMessage());
781             }
782         } else {
783             // Don't update ImsService for emergency MMTEL feature.
784             Log.i(LOG_TAG, "doesn't support emergency calling on slot " + featurePair.slotId);
785         }
786     }
787 
788     // This method should only be called when already synchronized on mLock.
789     // overridden by compat layer to create features
createImsFeature(int slotId, int subId, int featureType, long capabilities)790     protected IInterface createImsFeature(int slotId, int subId, int featureType, long capabilities)
791             throws RemoteException {
792         switch (featureType) {
793             case ImsFeature.FEATURE_MMTEL: {
794                 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
795                     boolean emergencyAvailable =
796                             (capabilities & ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL) > 0;
797                     if (emergencyAvailable) {
798                         return mIImsServiceController.createEmergencyOnlyMmTelFeature(slotId);
799                     } else {
800                         return null;
801                     }
802                 }
803                 return mIImsServiceController.createMmTelFeature(slotId, subId);
804             }
805             case ImsFeature.FEATURE_RCS: {
806                 return mIImsServiceController.createRcsFeature(slotId, subId);
807             }
808             default:
809                 return null;
810         }
811     }
812 
813     // This method should only be called when already synchronized on mLock.
addImsFeatureStatusCallback(int slotId, int featureType)814     private void addImsFeatureStatusCallback(int slotId, int featureType) throws RemoteException {
815         ImsFeatureStatusCallback c = new ImsFeatureStatusCallback(slotId, featureType);
816         mFeatureStatusCallbacks.add(c);
817         registerImsFeatureStatusCallback(slotId, featureType, c.getCallback());
818     }
819 
820     // This method should only be called when already synchronized on mLock.
removeImsFeatureStatusCallback(int slotId, int featureType)821     private void removeImsFeatureStatusCallback(int slotId, int featureType) {
822         ImsFeatureStatusCallback callbackToRemove = mFeatureStatusCallbacks.stream().filter(c ->
823                 c.mSlotId == slotId && c.mFeatureType == featureType).findFirst().orElse(null);
824         // Remove status callbacks from list.
825         if (callbackToRemove != null) {
826             mFeatureStatusCallbacks.remove(callbackToRemove);
827             unregisterImsFeatureStatusCallback(slotId, featureType, callbackToRemove.getCallback());
828         }
829     }
830 
831     // overridden by compat layer to register feature status callbacks
registerImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)832     protected void registerImsFeatureStatusCallback(int slotId, int featureType,
833             IImsFeatureStatusCallback c) throws RemoteException {
834         mIImsServiceController.addFeatureStatusCallback(slotId, featureType, c);
835     }
836 
837     // overridden by compat layer to deregister feature status callbacks
unregisterImsFeatureStatusCallback(int slotId, int featureType, IImsFeatureStatusCallback c)838     protected void unregisterImsFeatureStatusCallback(int slotId, int featureType,
839             IImsFeatureStatusCallback c) {
840         try {
841             mIImsServiceController.removeFeatureStatusCallback(slotId, featureType, c);
842         } catch (RemoteException e) {
843             mLocalLog.log("unregisterImsFeatureStatusCallback - couldn't remove " + c);
844         }
845     }
846 
847 
848     // overridden by compat layer to remove features
removeImsFeature(int slotId, int featureType, boolean changeSubId)849     protected void removeImsFeature(int slotId, int featureType, boolean changeSubId)
850             throws RemoteException {
851         mIImsServiceController.removeImsFeature(slotId, featureType, changeSubId);
852     }
853 
addImsFeatureBinder(int slotId, int subId, int featureType, IInterface b, long capabilities)854     private void addImsFeatureBinder(int slotId, int subId, int featureType, IInterface b,
855             long capabilities)
856             throws RemoteException {
857         if (b == null) {
858 
859             Log.w(LOG_TAG, "addImsFeatureBinder: null IInterface reported for "
860                     + ImsFeature.FEATURE_LOG_MAP.get(featureType));
861             mLocalLog.log("addImsFeatureBinder: null IInterface reported for "
862                     + ImsFeature.FEATURE_LOG_MAP.get(featureType));
863             return;
864         }
865         ImsFeatureContainer fc = createFeatureContainer(slotId, subId, b.asBinder(), capabilities);
866         mRepo.addConnection(slotId, subId, featureType, fc);
867     }
868 
removeImsFeatureBinder(int slotId, int featureType)869     private void removeImsFeatureBinder(int slotId, int featureType) {
870         mRepo.removeConnection(slotId, featureType);
871     }
872 
createFeatureContainer(int slotId, int subId, IBinder b, long capabilities)873     private ImsFeatureContainer createFeatureContainer(int slotId, int subId, IBinder b,
874             long capabilities)
875             throws RemoteException {
876         IImsConfig config = getConfig(slotId, subId);
877         IImsRegistration reg = getRegistration(slotId, subId);
878         // When either is null, this is an unexpected condition. Do not report the ImsService as
879         // being available.
880         if (config == null || reg == null) {
881             Log.w(LOG_TAG, "createFeatureContainer: invalid state. Reporting as not "
882                     + "available. componentName= " + getComponentName());
883             mLocalLog.log("createFeatureContainer: invalid state. Reporting as not "
884                     + "available.");
885             return null;
886         }
887         // SipTransport AIDL may be null for older devices, this is expected.
888         ISipTransport transport = getSipTransport(slotId);
889         return new ImsFeatureContainer(b, config, reg, transport, capabilities);
890     }
891 
getFeaturesForSlot(int slotId, Set<ImsFeatureConfiguration.FeatureSlotPair> features)892     private List<Integer> getFeaturesForSlot(int slotId,
893             Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
894         return features.stream().filter(f -> f.slotId == slotId).map(f -> f.featureType)
895                 .collect(Collectors.toList());
896     }
897 
cleanupAllFeatures()898     private void cleanupAllFeatures() {
899         synchronized (mLock) {
900             // Remove all features and clean up all associated Binders.
901             for (ImsFeatureConfiguration.FeatureSlotPair i : mImsFeatures) {
902                 removeImsServiceFeature(i, false);
903             }
904         }
905     }
906 
checkAndReportAnomaly(ComponentName name)907     private void checkAndReportAnomaly(ComponentName name) {
908         if (mPackageManager == null) {
909             Log.w(LOG_TAG, "mPackageManager null");
910             return;
911         }
912         ChangedPackages curChangedPackages =
913                             mPackageManager.getChangedPackages(mLastSequenceNumber);
914         if (curChangedPackages != null) {
915             mLastSequenceNumber = curChangedPackages.getSequenceNumber();
916             List<String> packagesNames = curChangedPackages.getPackageNames();
917             if (packagesNames.contains(name.getPackageName())) {
918                 Log.d(LOG_TAG, "Ignore due to updated, package: " + name.getPackageName());
919                 return;
920             }
921         }
922         String message = "IMS Service Crashed";
923         AnomalyReporter.reportAnomaly(mAnomalyUUID, message);
924     }
925 
926     @Override
toString()927     public String toString() {
928         synchronized (mLock) {
929             return "[ImsServiceController: componentName=" + getComponentName() + ", features="
930                     + mImsFeatures + ", isBinding=" + mIsBinding + ", isBound=" + mIsBound
931                     + ", serviceController=" + getImsServiceController() + ", rebindDelay="
932                     + getRebindDelay() + "]";
933         }
934     }
935 
dump(PrintWriter printWriter)936     public void dump(PrintWriter printWriter) {
937         mLocalLog.dump(printWriter);
938     }
939 }
940