blob: f18ad326da36b3e7ba6fb0ad94f6913eaea5c222 [file]
/*
* Copyright (C) 2016 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.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
import android.telecom.DefaultDialerManager;
import android.telecom.Log;
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.telecom.flags.FeatureFlags;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.function.IntConsumer;
public class DefaultDialerCache {
private static final String LOG_TAG = "DefaultDialerCache";
@VisibleForTesting
public final Handler mHandler = new Handler(Looper.getMainLooper());
private final Context mContext;
private final Context mAllUsersContext;
private final DefaultDialerManagerAdapter mDefaultDialerManagerAdapter;
private final ComponentName mSystemDialerComponentName;
private final RoleManagerAdapter mRoleManagerAdapter;
private final FeatureFlags mFeatureFlags;
private final ConcurrentHashMap<Integer, String> mCurrentDefaultDialerPerUser =
new ConcurrentHashMap<>();
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mHandler.post(() -> {
Log.startSession("DDC.oR");
try {
String packageName;
if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
packageName = null;
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
&& !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
packageName = intent.getData().getSchemeSpecificPart();
} else if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
packageName = null;
} else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
packageName = null;
} else {
return;
}
refreshCachesForUsersWithPackage(packageName);
} finally {
Log.endSession();
}
});
}
};
private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
int removedUser = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserUtil.USER_NULL);
if (removedUser == UserUtil.USER_NULL) {
Log.w(LOG_TAG, "Expected EXTRA_USER_HANDLE with ACTION_USER_REMOVED");
} else {
removeUserFromCache(removedUser);
Log.i(LOG_TAG, "Removing user %s", removedUser);
}
}
}
};
private ComponentName mOverrideSystemDialerComponentName;
public DefaultDialerCache(Context context,
DefaultDialerManagerAdapter defaultDialerManagerAdapter,
RoleManagerAdapter roleManagerAdapter,
TelecomSystem.SyncRoot lock, FeatureFlags featureFlags) {
mContext = context;
mDefaultDialerManagerAdapter = defaultDialerManagerAdapter;
mRoleManagerAdapter = roleManagerAdapter;
mFeatureFlags = featureFlags;
Resources resources = TelecomResourceId.getResources(mContext);
int resourceId = Resources.getSystem().getIdentifier("config_defaultDialer", "string",
"android");
String packageName = resources.getString(resourceId);
mSystemDialerComponentName = new ComponentName(packageName,
TelecomResourceId.getString(mContext, "incall_default_class"));
IntentFilter packageIntentFilter = new IntentFilter();
packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
packageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageIntentFilter.addDataScheme("package");
packageIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
// Important: retain the all users context or the receivers will not fire.
mAllUsersContext = context.createContextAsUser(UserHandle.ALL, 0);
mAllUsersContext.registerReceiver(mReceiver, packageIntentFilter,
Context.RECEIVER_NOT_EXPORTED);
IntentFilter bootIntentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
mAllUsersContext.registerReceiver(mReceiver, bootIntentFilter,
Context.RECEIVER_NOT_EXPORTED);
IntentFilter userRemovedFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
context.registerReceiver(mUserRemovedReceiver, userRemovedFilter);
}
public String[] getBTInCallServicePackages() {
return mRoleManagerAdapter.getBTInCallService();
}
public String getDefaultDialerApplication(UserHandle user) {
if (user.getIdentifier() < 0) {
Log.w(LOG_TAG, "Attempting to get default dialer for a meta-user %d",
user.getIdentifier());
return null;
}
// TODO: Re-enable this when we are able to use the cache once more. RoleManager does not
// provide a means for being informed when the role holder changes at the current time.
//
//synchronized (mLock) {
// String defaultDialer = mCurrentDefaultDialerPerUser.get(userId);
// if (!TextUtils.isEmpty(defaultDialer)) {
// return defaultDialer;
// }
//}
return refreshCacheForUserHandle(user);
}
public String getDefaultDialerApplicationLegacy(int userId) {
if (userId == UserHandle.CURRENT.getIdentifier()) {
userId = ActivityManager.getCurrentUser();
}
if (userId < 0) {
Log.w(LOG_TAG, "Attempting to get default dialer for a meta-user %d", userId);
return null;
}
// TODO: Re-enable this when we are able to use the cache once more. RoleManager does not
// provide a means for being informed when the role holder changes at the current time.
//
//synchronized (mLock) {
// String defaultDialer = mCurrentDefaultDialerPerUser.get(userId);
// if (!TextUtils.isEmpty(defaultDialer)) {
// return defaultDialer;
// }
//}
return refreshCacheForUserId(userId);
}
public String getDefaultDialerApplication() {
return getDefaultDialerApplication(mContext.getUser());
}
public void setSystemDialerComponentName(ComponentName testComponentName) {
Log.i(this, "setSystemDialerComponentName: %s", testComponentName);
mOverrideSystemDialerComponentName = testComponentName;
}
public String getSystemDialerApplication() {
if (mOverrideSystemDialerComponentName != null) {
return mOverrideSystemDialerComponentName.getPackageName();
}
return mSystemDialerComponentName.getPackageName();
}
public ComponentName getSystemDialerComponent() {
if (mOverrideSystemDialerComponentName != null) return mOverrideSystemDialerComponentName;
return mSystemDialerComponentName;
}
public ComponentName getDialtactsSystemDialerComponent() {
final Resources resources = TelecomResourceId.getResources(mContext);
return new ComponentName(getSystemDialerApplication(),
TelecomResourceId.getString(mContext, "dialer_default_class"));
}
public void observeDefaultDialerApplication(Executor executor, IntConsumer observer) {
mRoleManagerAdapter.observeDefaultDialerApp(executor, observer);
}
public boolean isDefaultOrSystemDialer(String packageName, int userId) {
String defaultDialer = getDefaultDialerApplication(UserHandle.of(userId));
return Objects.equals(packageName, defaultDialer)
|| Objects.equals(packageName, getSystemDialerApplication());
}
public boolean setDefaultDialer(String packageName, UserHandle user) {
boolean isChanged = mDefaultDialerManagerAdapter.setDefaultDialerApplication(
mContext, packageName, user);
if (isChanged) {
// Update the cache synchronously so that there is no delay in cache update.
mCurrentDefaultDialerPerUser.put(user.getIdentifier(),
packageName == null ? "" : packageName);
}
return isChanged;
}
private String refreshCacheForUserId(int userId) {
String currentDefaultDialer =
mRoleManagerAdapter.getDefaultDialerApp(userId);
mCurrentDefaultDialerPerUser.put(userId, currentDefaultDialer == null ? "" :
currentDefaultDialer);
return currentDefaultDialer;
}
private String refreshCacheForUserHandle(UserHandle user) {
String currentDefaultDialer =
mRoleManagerAdapter.getDefaultDialerAppFromUserHandle(user);
mCurrentDefaultDialerPerUser.put(user.getIdentifier(), currentDefaultDialer == null ? "" :
currentDefaultDialer);
return currentDefaultDialer;
}
/**
* Refreshes the cache for users that currently have packageName as their cached default dialer.
* If packageName is null, refresh all caches.
*
* @param packageName Name of the affected package.
*/
private void refreshCachesForUsersWithPackage(String packageName) {
mCurrentDefaultDialerPerUser.forEach((userId, currentName) -> {
if (packageName == null || Objects.equals(packageName, currentName)) {
String newDefaultDialer = refreshCacheForUserId(userId);
Log.v(LOG_TAG, "Refreshing default dialer for user %d: now %s",
userId, newDefaultDialer);
}
});
}
public void dumpCache(IndentingPrintWriter pw) {
pw.println("System Dialer: " + mSystemDialerComponentName);
if (mOverrideSystemDialerComponentName != null) {
pw.println("System Dialer (override): " + mOverrideSystemDialerComponentName);
}
mCurrentDefaultDialerPerUser.forEach((k, v) -> pw.printf("User %d: %s\n", k, v));
}
private void removeUserFromCache(int userId) {
mCurrentDefaultDialerPerUser.remove(userId);
}
public RoleManagerAdapter getRoleManagerAdapter() {
return mRoleManagerAdapter;
}
public interface DefaultDialerManagerAdapter {
String getDefaultDialerApplication(Context context);
String getDefaultDialerApplication(Context context, UserHandle user);
boolean setDefaultDialerApplication(Context context, String packageName, UserHandle user);
}
static class DefaultDialerManagerAdapterImpl implements DefaultDialerManagerAdapter {
@Override
public String getDefaultDialerApplication(Context context) {
return DefaultDialerManager.getDefaultDialerApplication(context);
}
@Override
public String getDefaultDialerApplication(Context context, UserHandle user) {
return DefaultDialerManager.getDefaultDialerApplication(context, user);
}
@Override
public boolean setDefaultDialerApplication(Context context, String packageName,
UserHandle user) {
return DefaultDialerManager.setDefaultDialerApplication(context, packageName, user);
}
}
}