content: /*
   * Copyright (C) 2014 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.server.telecom;
  
  import android.app.role.RoleManager;
  import android.content.BroadcastReceiver;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.content.ServiceConnection;
  import android.os.IBinder;
  import android.os.RemoteException;
  import android.os.ServiceManager;
  import android.os.UserHandle;
  import android.telecom.DefaultDialerManager;
  import android.telecom.PhoneAccountHandle;
  import android.telecom.TelecomManager;
  import android.telephony.CarrierConfigManager;
  import android.util.IntArray;
  import android.util.Slog;
  
  import com.android.internal.annotations.GuardedBy;
  import com.android.internal.telephony.SmsApplication;
  import com.android.server.LocalServices;
  import com.android.server.SystemService;
  import com.android.server.pm.UserManagerService;
  import com.android.server.pm.permission.DefaultPermissionGrantPolicy;
  import com.android.server.pm.permission.PermissionManagerServiceInternal;
  
  /**
   * Starts the telecom component by binding to its ITelecomService implementation. Telecom is setup
   * to run in the system-server process so once it is loaded into memory it will stay running.
   * @hide
   */
  public class TelecomLoaderService extends SystemService {
      private static final String TAG = "TelecomLoaderService";
  
      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);
                  ServiceManager.addService(Context.TELECOM_SERVICE, service);
  
                  synchronized (mLock) {
                      if (mDefaultSimCallManagerRequests != null) {
                          final DefaultPermissionGrantPolicy permissionPolicy =
                                  getDefaultPermissionGrantPolicy();
                          if (mDefaultSimCallManagerRequests != null) {
                              TelecomManager telecomManager =
                                  (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
                              PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager();
                              if (phoneAccount != null) {
                                  final int requestCount = mDefaultSimCallManagerRequests.size();
                                  final String packageName =
                                      phoneAccount.getComponentName().getPackageName();
                                  for (int i = requestCount - 1; i >= 0; i--) {
                                      final int userId = mDefaultSimCallManagerRequests.get(i);
                                      mDefaultSimCallManagerRequests.remove(i);
                                      permissionPolicy
                                              .grantDefaultPermissionsToDefaultSimCallManager(
                                                      packageName, userId);
                                  }
                              }
                          }
                      }
                  }
              } catch (RemoteException e) {
                  Slog.w(TAG, "Failed linking to death.");
              }
          }
  
          @Override
          public void onServiceDisconnected(ComponentName name) {
              connectToTelecom();
          }
      }
  
      private DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() {
          return LocalServices.getService(PermissionManagerServiceInternal.class)
                  .getDefaultPermissionGrantPolicy();
      }
  
      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";
  
      private final Object mLock = new Object();
  
      @GuardedBy("mLock")
      private IntArray mDefaultSimCallManagerRequests;
  
      private final Context mContext;
  
      @GuardedBy("mLock")
      private TelecomServiceConnection mServiceConnection;
  
      public TelecomLoaderService(Context context) {
          super(context);
          mContext = context;
          registerDefaultAppProviders();
      }
  
      @Override
      public void onStart() {
      }
  
      @Override
      public void onBootPhase(int phase) {
          if (phase == PHASE_ACTIVITY_MANAGER_READY) {
              registerDefaultAppNotifier();
              registerCarrierConfigChangedReceiver();
              connectToTelecom();
          }
      }
  
      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;
              }
          }
      }
  
  
      private void registerDefaultAppProviders() {
          final DefaultPermissionGrantPolicy permissionPolicy = getDefaultPermissionGrantPolicy();
  
          // Set a callback for the permission grant policy to query the default sms app.
          permissionPolicy.setSmsAppPackagesProvider(userId -> {
              synchronized (mLock) {
                  if (mServiceConnection == null) {
                      return null;
                  }
              }
              ComponentName smsComponent = SmsApplication.getDefaultSmsApplication(
                      mContext, true);
              if (smsComponent != null) {
                  return new String[]{smsComponent.getPackageName()};
              }
              return null;
          });
  
          // Set a callback for the permission grant policy to query the default dialer app.
          permissionPolicy.setDialerAppPackagesProvider(userId -> {
              synchronized (mLock) {
                  if (mServiceConnection == null) {
                      return null;
                  }
              }
              String packageName = DefaultDialerManager.getDefaultDialerApplication(mContext);
              if (packageName != null) {
                  return new String[]{packageName};
              }
              return null;
          });
  
          // Set a callback for the permission grant policy to query the default sim call manager.
          permissionPolicy.setSimCallManagerPackagesProvider(userId -> {
              synchronized (mLock) {
                  if (mServiceConnection == null) {
                      if (mDefaultSimCallManagerRequests == null) {
                          mDefaultSimCallManagerRequests = new IntArray();
                      }
                      mDefaultSimCallManagerRequests.add(userId);
                      return null;
                  }
              }
              TelecomManager telecomManager =
                      (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
              PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId);
              if (phoneAccount != null) {
                  return new String[]{phoneAccount.getComponentName().getPackageName()};
              }
              return null;
          });
      }
  
      private void registerDefaultAppNotifier() {
          final DefaultPermissionGrantPolicy permissionPolicy = getDefaultPermissionGrantPolicy();
          // Notify the package manager on default app changes
          final RoleManager roleManager = mContext.getSystemService(RoleManager.class);
          roleManager.addOnRoleHoldersChangedListenerAsUser(mContext.getMainExecutor(),
                  (roleName, user) -> updateSimCallManagerPermissions(permissionPolicy,
                          user.getIdentifier()), UserHandle.ALL);
      }
  
  
      private void registerCarrierConfigChangedReceiver() {
          BroadcastReceiver receiver = new BroadcastReceiver() {
              @Override
              public void onReceive(Context context, Intent intent) {
                  if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
                      for (int userId : UserManagerService.getInstance().getUserIds()) {
                          updateSimCallManagerPermissions(getDefaultPermissionGrantPolicy(), userId);
                      }
                  }
              }
          };
  
          mContext.registerReceiverAsUser(receiver, UserHandle.ALL,
              new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED), null, null);
      }
  
      private void updateSimCallManagerPermissions(
              DefaultPermissionGrantPolicy permissionGrantPolicy, int userId) {
          TelecomManager telecomManager =
              (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
          PhoneAccountHandle phoneAccount = telecomManager.getSimCallManager(userId);
          if (phoneAccount != null) {
              Slog.i(TAG, "updating sim call manager permissions for userId:" + userId);
              String packageName = phoneAccount.getComponentName().getPackageName();
              permissionGrantPolicy.grantDefaultPermissionsToDefaultSimCallManager(
                  packageName, userId);
          }
      }
  }
  