455 lines
20 KiB
Java
455 lines
20 KiB
Java
/*
|
|
* 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 com.android.internal.annotations.VisibleForTesting;
|
|
import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
|
|
import com.android.server.telecom.bluetooth.BluetoothRouteManager;
|
|
import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
|
|
import com.android.server.telecom.components.UserCallIntentProcessor;
|
|
import com.android.server.telecom.components.UserCallIntentProcessorFactory;
|
|
import com.android.server.telecom.ui.AudioProcessingNotification;
|
|
import com.android.server.telecom.ui.DisconnectedCallNotifier;
|
|
import com.android.server.telecom.ui.IncomingCallNotifier;
|
|
import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory;
|
|
import com.android.server.telecom.CallAudioManager.AudioServiceFactory;
|
|
import com.android.server.telecom.DefaultDialerCache.DefaultDialerManagerAdapter;
|
|
import com.android.server.telecom.ui.ToastFactory;
|
|
|
|
import android.app.ActivityManager;
|
|
import android.bluetooth.BluetoothManager;
|
|
import android.Manifest;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.content.ServiceConnection;
|
|
import android.content.pm.ApplicationInfo;
|
|
import android.content.pm.PackageManager;
|
|
import android.content.pm.ResolveInfo;
|
|
import android.net.Uri;
|
|
import android.os.UserHandle;
|
|
import android.os.UserManager;
|
|
import android.telecom.Log;
|
|
import android.telecom.PhoneAccountHandle;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import java.io.FileNotFoundException;
|
|
import java.io.InputStream;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Top-level Application class for Telecom.
|
|
*/
|
|
public class TelecomSystem {
|
|
|
|
/**
|
|
* This interface is implemented by system-instantiated components (e.g., Services and
|
|
* Activity-s) that wish to use the TelecomSystem but would like to be testable. Such a
|
|
* component should implement the getTelecomSystem() method to return the global singleton,
|
|
* and use its own method. Tests can subclass the component to return a non-singleton.
|
|
*
|
|
* A refactoring goal for Telecom is to limit use of the TelecomSystem singleton to those
|
|
* system-instantiated components, and have all other parts of the system just take all their
|
|
* dependencies as explicit arguments to their constructor or other methods.
|
|
*/
|
|
public interface Component {
|
|
TelecomSystem getTelecomSystem();
|
|
}
|
|
|
|
|
|
/**
|
|
* Tagging interface for the object used for synchronizing multi-threaded operations in
|
|
* the Telecom system.
|
|
*/
|
|
public interface SyncRoot {
|
|
}
|
|
|
|
private static final IntentFilter USER_SWITCHED_FILTER =
|
|
new IntentFilter(Intent.ACTION_USER_SWITCHED);
|
|
|
|
private static final IntentFilter USER_STARTING_FILTER =
|
|
new IntentFilter(Intent.ACTION_USER_STARTING);
|
|
|
|
private static final IntentFilter BOOT_COMPLETE_FILTER =
|
|
new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
|
|
|
|
/** Intent filter for dialer secret codes. */
|
|
private static final IntentFilter DIALER_SECRET_CODE_FILTER;
|
|
|
|
/**
|
|
* Initializes the dialer secret code intent filter. Setup to handle the various secret codes
|
|
* which can be dialed (e.g. in format *#*#code#*#*) to trigger various behavior in Telecom.
|
|
*/
|
|
static {
|
|
DIALER_SECRET_CODE_FILTER = new IntentFilter(
|
|
"android.provider.Telephony.SECRET_CODE");
|
|
DIALER_SECRET_CODE_FILTER.addDataScheme("android_secret_code");
|
|
DIALER_SECRET_CODE_FILTER
|
|
.addDataAuthority(DialerCodeReceiver.TELECOM_SECRET_CODE_DEBUG_ON, null);
|
|
DIALER_SECRET_CODE_FILTER
|
|
.addDataAuthority(DialerCodeReceiver.TELECOM_SECRET_CODE_DEBUG_OFF, null);
|
|
DIALER_SECRET_CODE_FILTER
|
|
.addDataAuthority(DialerCodeReceiver.TELECOM_SECRET_CODE_MARK, null);
|
|
DIALER_SECRET_CODE_FILTER
|
|
.addDataAuthority(DialerCodeReceiver.TELECOM_SECRET_CODE_MENU, null);
|
|
}
|
|
|
|
private static TelecomSystem INSTANCE = null;
|
|
|
|
private final SyncRoot mLock = new SyncRoot() { };
|
|
private final MissedCallNotifier mMissedCallNotifier;
|
|
private final IncomingCallNotifier mIncomingCallNotifier;
|
|
private final PhoneAccountRegistrar mPhoneAccountRegistrar;
|
|
private final CallsManager mCallsManager;
|
|
private final RespondViaSmsManager mRespondViaSmsManager;
|
|
private final Context mContext;
|
|
private final CallIntentProcessor mCallIntentProcessor;
|
|
private final TelecomBroadcastIntentProcessor mTelecomBroadcastIntentProcessor;
|
|
private final TelecomServiceImpl mTelecomServiceImpl;
|
|
private final ContactsAsyncHelper mContactsAsyncHelper;
|
|
private final DialerCodeReceiver mDialerCodeReceiver;
|
|
|
|
private boolean mIsBootComplete = false;
|
|
|
|
private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
Log.startSession("TSSwR.oR");
|
|
try {
|
|
synchronized (mLock) {
|
|
int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
|
|
UserHandle currentUserHandle = new UserHandle(userHandleId);
|
|
mPhoneAccountRegistrar.setCurrentUserHandle(currentUserHandle);
|
|
mCallsManager.onUserSwitch(currentUserHandle);
|
|
}
|
|
} finally {
|
|
Log.endSession();
|
|
}
|
|
}
|
|
};
|
|
|
|
private final BroadcastReceiver mUserStartingReceiver = new BroadcastReceiver() {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
Log.startSession("TSStR.oR");
|
|
try {
|
|
synchronized (mLock) {
|
|
int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
|
|
UserHandle addingUserHandle = new UserHandle(userHandleId);
|
|
mCallsManager.onUserStarting(addingUserHandle);
|
|
}
|
|
} finally {
|
|
Log.endSession();
|
|
}
|
|
}
|
|
};
|
|
|
|
private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
|
|
@Override
|
|
public void onReceive(Context context, Intent intent) {
|
|
Log.startSession("TSBCR.oR");
|
|
try {
|
|
synchronized (mLock) {
|
|
mIsBootComplete = true;
|
|
mCallsManager.onBootCompleted();
|
|
}
|
|
} finally {
|
|
Log.endSession();
|
|
}
|
|
}
|
|
};
|
|
|
|
public static TelecomSystem getInstance() {
|
|
return INSTANCE;
|
|
}
|
|
|
|
public static void setInstance(TelecomSystem instance) {
|
|
if (INSTANCE != null) {
|
|
Log.w("TelecomSystem", "Attempt to set TelecomSystem.INSTANCE twice");
|
|
}
|
|
Log.i(TelecomSystem.class, "TelecomSystem.INSTANCE being set");
|
|
INSTANCE = instance;
|
|
}
|
|
|
|
public TelecomSystem(
|
|
Context context,
|
|
MissedCallNotifierImplFactory missedCallNotifierImplFactory,
|
|
CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
|
|
HeadsetMediaButtonFactory headsetMediaButtonFactory,
|
|
ProximitySensorManagerFactory proximitySensorManagerFactory,
|
|
InCallWakeLockControllerFactory inCallWakeLockControllerFactory,
|
|
AudioServiceFactory audioServiceFactory,
|
|
ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory
|
|
connectionServiceFocusManagerFactory,
|
|
Timeouts.Adapter timeoutsAdapter,
|
|
AsyncRingtonePlayer asyncRingtonePlayer,
|
|
PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
|
|
IncomingCallNotifier incomingCallNotifier,
|
|
InCallTonePlayer.ToneGeneratorFactory toneGeneratorFactory,
|
|
CallAudioRouteStateMachine.Factory callAudioRouteStateMachineFactory,
|
|
CallAudioModeStateMachine.Factory callAudioModeStateMachineFactory,
|
|
ClockProxy clockProxy,
|
|
RoleManagerAdapter roleManagerAdapter,
|
|
ContactsAsyncHelper.Factory contactsAsyncHelperFactory,
|
|
DeviceIdleControllerAdapter deviceIdleControllerAdapter) {
|
|
mContext = context.getApplicationContext();
|
|
LogUtils.initLogging(mContext);
|
|
DefaultDialerManagerAdapter defaultDialerAdapter =
|
|
new DefaultDialerCache.DefaultDialerManagerAdapterImpl();
|
|
|
|
DefaultDialerCache defaultDialerCache = new DefaultDialerCache(mContext,
|
|
defaultDialerAdapter, roleManagerAdapter, mLock);
|
|
|
|
Log.startSession("TS.init");
|
|
// Wrap this in a try block to ensure session cleanup occurs in the case of error.
|
|
try {
|
|
mPhoneAccountRegistrar = new PhoneAccountRegistrar(mContext, mLock, defaultDialerCache,
|
|
packageName -> AppLabelProxy.Util.getAppLabel(
|
|
mContext.getPackageManager(), packageName));
|
|
|
|
mContactsAsyncHelper = contactsAsyncHelperFactory.create(
|
|
new ContactsAsyncHelper.ContentResolverAdapter() {
|
|
@Override
|
|
public InputStream openInputStream(Context context, Uri uri)
|
|
throws FileNotFoundException {
|
|
return context.getContentResolver().openInputStream(uri);
|
|
}
|
|
});
|
|
BluetoothDeviceManager bluetoothDeviceManager = new BluetoothDeviceManager(mContext,
|
|
mContext.getSystemService(BluetoothManager.class).getAdapter());
|
|
BluetoothRouteManager bluetoothRouteManager = new BluetoothRouteManager(mContext, mLock,
|
|
bluetoothDeviceManager, new Timeouts.Adapter());
|
|
BluetoothStateReceiver bluetoothStateReceiver = new BluetoothStateReceiver(
|
|
bluetoothDeviceManager, bluetoothRouteManager);
|
|
mContext.registerReceiver(bluetoothStateReceiver, BluetoothStateReceiver.INTENT_FILTER);
|
|
|
|
WiredHeadsetManager wiredHeadsetManager = new WiredHeadsetManager(mContext);
|
|
SystemStateHelper systemStateHelper = new SystemStateHelper(mContext, mLock);
|
|
|
|
mMissedCallNotifier = missedCallNotifierImplFactory
|
|
.makeMissedCallNotifierImpl(mContext, mPhoneAccountRegistrar,
|
|
defaultDialerCache,
|
|
deviceIdleControllerAdapter);
|
|
DisconnectedCallNotifier.Factory disconnectedCallNotifierFactory =
|
|
new DisconnectedCallNotifier.Default();
|
|
|
|
CallerInfoLookupHelper callerInfoLookupHelper =
|
|
new CallerInfoLookupHelper(context, callerInfoAsyncQueryFactory,
|
|
mContactsAsyncHelper, mLock);
|
|
|
|
EmergencyCallHelper emergencyCallHelper = new EmergencyCallHelper(mContext,
|
|
defaultDialerCache, timeoutsAdapter);
|
|
|
|
InCallControllerFactory inCallControllerFactory = new InCallControllerFactory() {
|
|
@Override
|
|
public InCallController create(Context context, SyncRoot lock,
|
|
CallsManager callsManager, SystemStateHelper systemStateProvider,
|
|
DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter,
|
|
EmergencyCallHelper emergencyCallHelper) {
|
|
return new InCallController(context, lock, callsManager, systemStateProvider,
|
|
defaultDialerCache, timeoutsAdapter, emergencyCallHelper,
|
|
new CarModeTracker(), clockProxy);
|
|
}
|
|
};
|
|
|
|
CallDiagnosticServiceController callDiagnosticServiceController =
|
|
new CallDiagnosticServiceController(
|
|
new CallDiagnosticServiceController.ContextProxy() {
|
|
@Override
|
|
public List<ResolveInfo> queryIntentServicesAsUser(
|
|
@NonNull Intent intent, int flags, int userId) {
|
|
return mContext.getPackageManager().queryIntentServicesAsUser(
|
|
intent, flags, userId);
|
|
}
|
|
|
|
@Override
|
|
public boolean bindServiceAsUser(@NonNull Intent service,
|
|
@NonNull ServiceConnection conn, int flags,
|
|
@NonNull UserHandle user) {
|
|
return mContext.bindServiceAsUser(service, conn, flags, user);
|
|
}
|
|
|
|
@Override
|
|
public void unbindService(@NonNull ServiceConnection conn) {
|
|
mContext.unbindService(conn);
|
|
}
|
|
|
|
@Override
|
|
public UserHandle getCurrentUserHandle() {
|
|
return mCallsManager.getCurrentUserHandle();
|
|
}
|
|
},
|
|
mContext.getResources().getString(
|
|
com.android.server.telecom.R.string
|
|
.call_diagnostic_service_package_name),
|
|
mLock
|
|
);
|
|
|
|
AudioProcessingNotification audioProcessingNotification =
|
|
new AudioProcessingNotification(mContext);
|
|
|
|
ToastFactory toastFactory = new ToastFactory() {
|
|
@Override
|
|
public Toast makeText(Context context, int resId, int duration) {
|
|
return Toast.makeText(context, context.getMainLooper(),
|
|
context.getString(resId),
|
|
duration);
|
|
}
|
|
|
|
@Override
|
|
public Toast makeText(Context context, CharSequence text, int duration) {
|
|
return Toast.makeText(context, context.getMainLooper(), text, duration);
|
|
}
|
|
};
|
|
|
|
mCallsManager = new CallsManager(
|
|
mContext,
|
|
mLock,
|
|
callerInfoLookupHelper,
|
|
mMissedCallNotifier,
|
|
disconnectedCallNotifierFactory,
|
|
mPhoneAccountRegistrar,
|
|
headsetMediaButtonFactory,
|
|
proximitySensorManagerFactory,
|
|
inCallWakeLockControllerFactory,
|
|
connectionServiceFocusManagerFactory,
|
|
audioServiceFactory,
|
|
bluetoothRouteManager,
|
|
wiredHeadsetManager,
|
|
systemStateHelper,
|
|
defaultDialerCache,
|
|
timeoutsAdapter,
|
|
asyncRingtonePlayer,
|
|
phoneNumberUtilsAdapter,
|
|
emergencyCallHelper,
|
|
toneGeneratorFactory,
|
|
clockProxy,
|
|
audioProcessingNotification,
|
|
bluetoothStateReceiver,
|
|
callAudioRouteStateMachineFactory,
|
|
callAudioModeStateMachineFactory,
|
|
inCallControllerFactory,
|
|
callDiagnosticServiceController,
|
|
roleManagerAdapter,
|
|
toastFactory);
|
|
|
|
mIncomingCallNotifier = incomingCallNotifier;
|
|
incomingCallNotifier.setCallsManagerProxy(new IncomingCallNotifier.CallsManagerProxy() {
|
|
@Override
|
|
public boolean hasUnholdableCallsForOtherConnectionService(
|
|
PhoneAccountHandle phoneAccountHandle) {
|
|
return mCallsManager.hasUnholdableCallsForOtherConnectionService(
|
|
phoneAccountHandle);
|
|
}
|
|
|
|
@Override
|
|
public int getNumUnholdableCallsForOtherConnectionService(
|
|
PhoneAccountHandle phoneAccountHandle) {
|
|
return mCallsManager.getNumUnholdableCallsForOtherConnectionService(
|
|
phoneAccountHandle);
|
|
}
|
|
|
|
@Override
|
|
public Call getActiveCall() {
|
|
return mCallsManager.getActiveCall();
|
|
}
|
|
});
|
|
mCallsManager.setIncomingCallNotifier(mIncomingCallNotifier);
|
|
|
|
mRespondViaSmsManager = new RespondViaSmsManager(mCallsManager, mLock);
|
|
mCallsManager.setRespondViaSmsManager(mRespondViaSmsManager);
|
|
|
|
mContext.registerReceiverAsUser(mUserSwitchedReceiver, UserHandle.ALL,
|
|
USER_SWITCHED_FILTER, null, null);
|
|
mContext.registerReceiverAsUser(mUserStartingReceiver, UserHandle.ALL,
|
|
USER_STARTING_FILTER, null, null);
|
|
mContext.registerReceiverAsUser(mBootCompletedReceiver, UserHandle.ALL,
|
|
BOOT_COMPLETE_FILTER, null, null);
|
|
|
|
// Set current user explicitly since USER_SWITCHED_FILTER intent can be missed at
|
|
// startup
|
|
synchronized (mLock) {
|
|
UserHandle currentUserHandle = UserHandle.of(ActivityManager.getCurrentUser());
|
|
mPhoneAccountRegistrar.setCurrentUserHandle(currentUserHandle);
|
|
mCallsManager.onUserSwitch(currentUserHandle);
|
|
}
|
|
|
|
mCallIntentProcessor = new CallIntentProcessor(mContext, mCallsManager,
|
|
defaultDialerCache);
|
|
mTelecomBroadcastIntentProcessor = new TelecomBroadcastIntentProcessor(
|
|
mContext, mCallsManager);
|
|
|
|
// Register the receiver for the dialer secret codes, used to enable extended logging.
|
|
mDialerCodeReceiver = new DialerCodeReceiver(mCallsManager);
|
|
mContext.registerReceiver(mDialerCodeReceiver, DIALER_SECRET_CODE_FILTER,
|
|
Manifest.permission.CONTROL_INCALL_EXPERIENCE, null);
|
|
|
|
// There is no USER_SWITCHED broadcast for user 0, handle it here explicitly.
|
|
final UserManager userManager = UserManager.get(mContext);
|
|
mTelecomServiceImpl = new TelecomServiceImpl(
|
|
mContext, mCallsManager, mPhoneAccountRegistrar,
|
|
new CallIntentProcessor.AdapterImpl(defaultDialerCache),
|
|
new UserCallIntentProcessorFactory() {
|
|
@Override
|
|
public UserCallIntentProcessor create(Context context,
|
|
UserHandle userHandle) {
|
|
return new UserCallIntentProcessor(context, userHandle);
|
|
}
|
|
},
|
|
defaultDialerCache,
|
|
new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
|
|
new TelecomServiceImpl.SettingsSecureAdapterImpl(),
|
|
mLock);
|
|
} finally {
|
|
Log.endSession();
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public PhoneAccountRegistrar getPhoneAccountRegistrar() {
|
|
return mPhoneAccountRegistrar;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public CallsManager getCallsManager() {
|
|
return mCallsManager;
|
|
}
|
|
|
|
public CallIntentProcessor getCallIntentProcessor() {
|
|
return mCallIntentProcessor;
|
|
}
|
|
|
|
public TelecomBroadcastIntentProcessor getTelecomBroadcastIntentProcessor() {
|
|
return mTelecomBroadcastIntentProcessor;
|
|
}
|
|
|
|
public TelecomServiceImpl getTelecomServiceImpl() {
|
|
return mTelecomServiceImpl;
|
|
}
|
|
|
|
public Object getLock() {
|
|
return mLock;
|
|
}
|
|
|
|
public boolean isBootComplete() {
|
|
return mIsBootComplete;
|
|
}
|
|
}
|