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