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