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