/* * 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.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.pm.PackageManagerInternal; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; 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 android.util.SparseBooleanArray; 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; /** * 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 (mDefaultSmsAppRequests != null || mDefaultDialerAppRequests != null || mDefaultSimCallManagerRequests != null) { final PackageManagerInternal packageManagerInternal = LocalServices .getService(PackageManagerInternal.class); if (mDefaultSmsAppRequests != null) { ComponentName smsComponent = SmsApplication.getDefaultSmsApplication( mContext, true); if (smsComponent != null) { final int requestCount = mDefaultSmsAppRequests.size(); for (int i = requestCount - 1; i >= 0; i--) { final int userid = mDefaultSmsAppRequests.get(i); mDefaultSmsAppRequests.remove(i); packageManagerInternal.grantDefaultPermissionsToDefaultSmsApp( smsComponent.getPackageName(), userid); } } } if (mDefaultDialerAppRequests != null) { String packageName = DefaultDialerManager.getDefaultDialerApplication( mContext); if (packageName != null) { final int requestCount = mDefaultDialerAppRequests.size(); for (int i = requestCount - 1; i >= 0; i--) { final int userId = mDefaultDialerAppRequests.get(i); mDefaultDialerAppRequests.remove(i); packageManagerInternal.grantDefaultPermissionsToDefaultDialerApp( packageName, userId); } } } 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); packageManagerInternal .grantDefaultPermissionsToDefaultSimCallManager( packageName, userId); } } } } } } catch (RemoteException e) { Slog.w(TAG, "Failed linking to death."); } } @Override public void onServiceDisconnected(ComponentName name) { connectToTelecom(); } } 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 mDefaultSmsAppRequests; @GuardedBy("mLock") private IntArray mDefaultDialerAppRequests; @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 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) { if (mServiceConnection == null) { if (mDefaultSmsAppRequests == null) { mDefaultSmsAppRequests = new IntArray(); } mDefaultSmsAppRequests.add(userId); return null; } } 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) { if (mServiceConnection == null) { if (mDefaultDialerAppRequests == null) { mDefaultDialerAppRequests = new IntArray(); } mDefaultDialerAppRequests.add(userId); return null; } } 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) { 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 PackageManagerInternal packageManagerInternal = LocalServices.getService( PackageManagerInternal.class); // Notify the package manager on default app changes final Uri defaultSmsAppUri = Settings.Secure.getUriFor( Settings.Secure.SMS_DEFAULT_APPLICATION); final Uri defaultDialerAppUri = Settings.Secure.getUriFor( Settings.Secure.DIALER_DEFAULT_APPLICATION); ContentObserver contentObserver = new ContentObserver( new Handler(Looper.getMainLooper())) { @Override public void onChange(boolean selfChange, Uri uri, int userId) { if (defaultSmsAppUri.equals(uri)) { ComponentName smsComponent = SmsApplication.getDefaultSmsApplication( mContext, true); if (smsComponent != null) { packageManagerInternal.grantDefaultPermissionsToDefaultSmsApp( smsComponent.getPackageName(), userId); } } else if (defaultDialerAppUri.equals(uri)) { String packageName = DefaultDialerManager.getDefaultDialerApplication( mContext); if (packageName != null) { packageManagerInternal.grantDefaultPermissionsToDefaultDialerApp( packageName, userId); } updateSimCallManagerPermissions(packageManagerInternal, userId); } } }; mContext.getContentResolver().registerContentObserver(defaultSmsAppUri, false, contentObserver, UserHandle.USER_ALL); mContext.getContentResolver().registerContentObserver(defaultDialerAppUri, false, contentObserver, UserHandle.USER_ALL); } private void registerCarrierConfigChangedReceiver() { final PackageManagerInternal packageManagerInternal = LocalServices.getService( PackageManagerInternal.class); 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(packageManagerInternal, userId); } } } }; mContext.registerReceiverAsUser(receiver, UserHandle.ALL, new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED), null, null); } private void updateSimCallManagerPermissions(PackageManagerInternal packageManagerInternal, 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(); packageManagerInternal.grantDefaultPermissionsToDefaultSimCallManager( packageName, userId); } } }