Principle analysis of Android system service TelecomService startup process

  • 2021-12-04 19:52:45
  • OfStack

Since 1 is responsible for the development of Android Telephony, Telecom service and UI, which are the upper parts of the communication process, have not been carefully studied. Recently, I happened to encounter a call problem, which involved Telecom, so I took the time to carefully study the related code. Here is a brief summary. This article mainly consists of the following two parts:

What is an Telecom service? What is its function? The Startup and Initialization Process of Telecom Module;

In the next article, taking the actual call process as an example, it analyzes how telephony sends telephone information to Telecom module after receiving incoming calls and how Telecom handles incoming calls.

What is an Telecom service

Telecom is a system service of Android. Its main function is to manage the current calls of Android system, such as caller ID, answering and hanging up the phone, etc. It plays a bridge role between Telephony module and upper UI. For example, when Telephony receives a new incoming call, it will first inform Telecom, and then Telecom service will inform the upper application of incoming call information and display the incoming call interface.

Telecom service provides an interface class TelecomManager. Through the interface provided by Telecom service, clients can query call status, send call requests and add call links.

According to the AndroidManifest. xml file corresponding to Telecom process, ID, the user of Telecom process, is the same as ID, the system process user, and is the core service of the system. So, what does the attribute value android: process = "system" mean? Looking at the official documentation, this indicates that Telecom will be started in the process system, so that resources can be shared with other processes (for the global process Android, this is the process where SystemServer is located).

android:process

By setting this attribute to a a name that 's shared with another application can can arrange components same same same same same same same process process-but user user share user user 3EN the same certificate.

If the name assigned this attribute begins with a colon (''), a new process, private to the application, is created when it 's needed. If the begins with a character, a global 6EN name is created. A global process can be shared with other applications, reducing resource usage.


 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
   package="com.android.server.telecom"
   android:versionCode="1"
   android:versionName="1.0.0"
   coreApp="true"
   android:sharedUserId="android.uid.system">

   <application android:label="@string/telecommAppLabel"
    android:icon="@mipmap/ic_launcher_phone"
    android:allowBackup="false"
    android:supportsRtl="true"
    android:process="system"
    android:usesCleartextTraffic="false"
    android:defaultToDeviceProtectedStorage="true"
    android:directBootAware="true">
   ....
   //  Include TelecomService
   <service android:name=".components.TelecomService"
     android:singleUser="true"
     android:process="system">
    <intent-filter>
     <action android:name="android.telecom.ITelecomService" />
    </intent-filter>
   </service>
  ....
  </application>
 </manifest>

Code path:

/android/applications/sources/services/Telecomm/
/android/frameworks/base/telecomm/

Once you know what an Telecom service is, let's look at 1 to see how an Telecom service is started and initialized.

Startup and initialization of Telecom process

After the SystemServer process initializes and starts the core services of the system, such as ActivityManagerService, other services of the system will be loaded, including one system service related to Telecom service startup, which is specially used to load Telecom:


 private void startOtherServices() {
  ....
  // Start TelecomLoaderService System service for loading Telecom
  mSystemServiceManager.startService(TelecomLoaderService.class);
  //  Start telephony Registration service, which is used to register listening telephony Interface of state 
  telephonyRegistry = new TelephonyRegistry(context);
  ServiceManager.addService("telephony.registry", telephonyRegistry);
 }

Call the interface startService of the system service steward SystemServiceManager to create a new service, register it in the system, and finally call onStart () to start the service.


 public class SystemServiceManager {

  @SuppressWarnings("unchecked")
  public SystemService startService(String className) {
   final Class<SystemService> serviceClass;
   try {
    serviceClass = (Class<SystemService>)Class.forName(className);
   } catch (ClassNotFoundException ex) {
    ....
   }
   return startService(serviceClass);
  }

  //  Serviced class File to create a new service object ( Services must inherit SystemService)
  @SuppressWarnings("unchecked")
  public <T extends SystemService> T startService(Class<T> serviceClass) {
   try {
    final String name = serviceClass.getName();
    Slog.i(TAG, "Starting " + name);
    Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);

    // Create the service.
    if (!SystemService.class.isAssignableFrom(serviceClass)) {
     throw new RuntimeException("Failed to create " + name
       + ": service must extend " + SystemService.class.getName());
    }
    final T service;
    try {
     Constructor<T> constructor = serviceClass.getConstructor(Context.class);
     service = constructor.newInstance(mContext);
    } catch (InstantiationException ex) {
     throw new RuntimeException("Failed to create service " + name
       + ": service could not be instantiated", ex);
    } 
    ....
    // Register it.
    mServices.add(service);

    // Start it.
    try {
     service.onStart();
    } catch (RuntimeException ex) {
     throw new RuntimeException("Failed to start service " + name
       + ": onStart threw an exception", ex);
    }
    return service;
   } finally {
    Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
   }
  }
 }

Create an TelecomLoaderService system service and inform PackageManagerService (PMS) of the default SMS application, dial-up application and SIM call management application (I don't know what this is), so that the application can be found at the appropriate time.


 public class TelecomLoaderService extends SystemService {
  ...
  public TelecomLoaderService(Context context) {
   super(context);
   mContext = context;
   registerDefaultAppProviders();
  }

  @Override
  public void onStart() {
  }

  private void registerDefaultAppProviders() {
   final PackageManagerInternal packageManagerInternal = LocalServices.getService(
     PackageManagerInternal.class);

   // Set a callback for the package manager to query the default sms app.
   packageManagerInternal.setSmsAppPackagesProvider(
     new PackageManagerInternal.PackagesProvider() {
    @Override
    public String[] getPackages(int userId) {
     synchronized (mLock) {
     ....
     ComponentName smsComponent = SmsApplication.getDefaultSmsApplication(
       mContext, true);
     if (smsComponent != null) {
      return new String[]{smsComponent.getPackageName()};
     }
     return null;
    }
   });

   // Set a callback for the package manager to query the default dialer app.
   packageManagerInternal.setDialerAppPackagesProvider(
     new PackageManagerInternal.PackagesProvider() {
    @Override
    public String[] getPackages(int userId) {
     synchronized (mLock) {
     ....
     String packageName = DefaultDialerManager.getDefaultDialerApplication(mContext);
     if (packageName != null) {
      return new String[]{packageName};
     }
     return null;
    }
   });

   // Set a callback for the package manager to query the default sim call manager.
   packageManagerInternal.setSimCallManagerPackagesProvider(
     new PackageManagerInternal.PackagesProvider() {
    @Override
    public String[] getPackages(int userId) {
     synchronized (mLock) {
     ....
     TelecomManager telecomManager =
      (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
     PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId);
     if (phoneAccount != null) {
      return new String[]{phoneAccount.getComponentName().getPackageName()};
     }
     return null;
    }
   });
  }
 }

Up to now, it seems that Telecom service has not started, so where did Telecom service start? Look carefully at the source code of TelecomLoaderService, which has a function of onBootPhase for SystemServer to inform the system service at the current stage of system startup. As you can see here, after (ActivityManagerService) AMS is started, you can start connecting to Telecom service:

First, register the default application (SMS/Dialer etc) notification object so that these applications can send changes (for example, when a third-party SMS application is downloaded, the system can be notified of this change); Then, the registered operator configures the changed broadcast receiver, and if the configuration changes, the system will receive notification; Bind TelecomService and register it in the system.

 public class TelecomLoaderService extends SystemService {

  private static final ComponentName SERVICE_COMPONENT = new ComponentName(
    "com.android.server.telecom",
    "com.android.server.telecom.components.TelecomService");

  private static final String SERVICE_ACTION = "com.android.ITelecomService";

  //  The stage of the current system startup 
  @Override
  public void onBootPhase(int phase) {
   if (phase == PHASE_ACTIVITY_MANAGER_READY) {
    registerDefaultAppNotifier();
    registerCarrierConfigChangedReceiver();
    connectToTelecom();
   }
  }

  // Binding Telecom Services 
  private void connectToTelecom() {
   synchronized (mLock) {
    if (mServiceConnection != null) {
     // TODO: Is unbinding worth doing or wait for system to rebind?
     mContext.unbindService(mServiceConnection);
     mServiceConnection = null;
    }

    TelecomServiceConnection serviceConnection = new TelecomServiceConnection();
    Intent intent = new Intent(SERVICE_ACTION);
    intent.setComponent(SERVICE_COMPONENT);
    int flags = Context.BIND_IMPORTANT | Context.BIND_FOREGROUND_SERVICE
      | Context.BIND_AUTO_CREATE;

    // Bind to Telecom and register the service
    if (mContext.bindServiceAsUser(intent, serviceConnection, flags, UserHandle.SYSTEM)) {
     mServiceConnection = serviceConnection;
    }
   }
  }
 }

Service binding: https://developer.android.com/guide/components/bound-services.html

Add the service to the ServiceManager and reconnect if the Telecom service connection is broken:


 public class TelecomLoaderService extends SystemService {

  private class TelecomServiceConnection implements ServiceConnection {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
     // Normally, we would listen for death here, but since telecom runs in the same process
     // as this loader (process="system") thats redundant here.
     try {
      service.linkToDeath(new IBinder.DeathRecipient() {
       @Override
       public void binderDied() {
        connectToTelecom();
       }
      }, 0);
      SmsApplication.getDefaultMmsApplication(mContext, false);
      // Add Telecom Services 
      ServiceManager.addService(Context.TELECOM_SERVICE, service);
      ....
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
     connectToTelecom();
    }
   }
 }

When binding services, call the onBind interface of TelecomService, initialize the entire Telecom system, and return one IBinder interface:


 /**
  * Implementation of the ITelecom interface.
  */
 public class TelecomService extends Service implements TelecomSystem.Component {

  @Override
  public IBinder onBind(Intent intent) {
   //  Initialize the entire Telecom System 
   initializeTelecomSystem(this);
   // Return IBinder Interface 
   synchronized (getTelecomSystem().getLock()) {
    return getTelecomSystem().getTelecomServiceImpl().getBinder();
   }
  }

 }

Telecom system initialization, the main work is to create a new TelecomSystem class, in this class, the whole Telecom service related classes are initialized:


 static void initializeTelecomSystem(Context context) {
   if (TelecomSystem.getInstance() == null) {

    final NotificationManager notificationManager =
      (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    //  Used to get contacts 
    contactInfoHelper = new ContactInfoHelper(context);
    //  New 1 Objects of singleton pattern 
    TelecomSystem.setInstance(new TelecomSystem(....));
   }
   ....
  }
 }

Construct a singleton TelecomSystem object:


 public TelecomSystem(
    Context context,
    /*  User missed call notification class (excluding received or rejected calls)  */
    MissedCallNotifierImplFactory missedCallNotifierImplFactory,
    /*  Inquire incoming call information  */
    CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
    /*  Headphone access status monitoring  */
    HeadsetMediaButtonFactory headsetMediaButtonFactory,
    /*  Distance sensor management  */
    ProximitySensorManagerFactory proximitySensorManagerFactory,
    /*  Telephone management during call  */
    InCallWakeLockControllerFactory inCallWakeLockControllerFactory,
    /*  Audio service management  */
    AudioServiceFactory audioServiceFactory,
    /*  Bluetooth device management  */
    BluetoothPhoneServiceImplFactory bluetoothPhoneServiceImplFactory,
    BluetoothVoIPServiceImplFactory bluetoothVoIPServiceImplFactory,
    /*  Query all timeout information  */
    Timeouts.Adapter timeoutsAdapter,
    /*  Bell play  */
    AsyncRingtonePlayer asyncRingtonePlayer,
    /*  Phone number help class  */
    PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
    /*  Blocking notification during call  */
    InterruptionFilterProxy interruptionFilterProxy) {
   mContext = context.getApplicationContext();
   //  Initialization telecom Relative feature
   TelecomFeature.makeFeature(mContext);
   //  Initialization telecom Database of 
   TelecomSystemDB.initialize(mContext);
   //  Create 1 A PhoneAccount Registration management class 
   mPhoneAccountRegistrar = new PhoneAccountRegistrar(mContext);
   ....
   //  Initialize the call steward, which is responsible for working with the upper layer UI Interaction of 
   mCallsManager = new CallsManager(
     mContext, mLock, mContactsAsyncHelper,
     callerInfoAsyncQueryFactory, mMissedCallNotifier,
     mPhoneAccountRegistrar, headsetMediaButtonFactory,
     proximitySensorManagerFactory, inCallWakeLockControllerFactory,
     audioServiceFactory, bluetoothManager,
     wiredHeadsetManager, systemStateProvider,
     defaultDialerAdapter, timeoutsAdapter,AsyncRingtonePlayer,
     phoneNumberUtilsAdapter, interruptionFilterProxy);

   CallsManager.initialize(mCallsManager);
   //  Register broadcasts to be received  
   mContext.registerReceiver(mUserSwitchedReceiver, USER_SWITCHED_FILTER);
   mContext.registerReceiver(mUserStartingReceiver, USER_STARTING_FILTER);
   mContext.registerReceiver(mFeatureChangedReceiver, FEATURE_CHANGED_FILTER);
   mContext.registerReceiver(mEmergencyReceiver, EMERGENCY_STATE_CHANGED);
   .... 
   //  Transfer station for all incoming and outgoing calls 
   mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager);
   //  Create 1 A TelecomServiceImpl Used to call TelecomService Interface of 
   mTelecomServiceImpl = new TelecomServiceImpl(
     mContext, mCallsManager, mPhoneAccountRegistrar,
     new CallIntentProcessor.AdapterImpl(),
     new UserCallIntentProcessorFactory() {
      @Override
      public UserCallIntentProcessor create(Context context, UserHandle userHandle) {
       return new UserCallIntentProcessor(context, userHandle);
      }
     },
     defaultDialerAdapter,
     new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
     mLock);
   //  Perform specific initialization operations 
   initialize(mContext);
  }
 }

What role does PhoneAccount in Android Telephony play? According to the instructions in the source code, PhoneAccount represents different ways to answer or make calls. For example, users can make calls through SIM cards, video calls, or an emergency call, and even dial through the internal interface of telephony, while Android distinguishes these call modes through PhoneAccount. The corresponding class PhoneAccountHandle is used to indicate which user is using the call service.


Related articles: