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