blob: 06565cd9a901e5b440b3a685f1afd0c0d69ab934 [file] [log] [blame]
/*
* Copyright (C) 2017 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.providers.contacts;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver.PendingResult;
import android.content.ContentProvider;
import android.content.Context;
import android.content.IContentProvider;
import android.content.Intent;
import android.content.IntentFilter;
import android.provider.ContactsContract;
import android.provider.VoicemailContract;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import com.android.providers.contacts.util.PackageUtils;
import com.google.common.annotations.VisibleForTesting;
/**
* - Handles package related broadcasts.
* - Also scan changed packages while the process wasn't running using PM.getChangedPackages().
*/
public class ContactsPackageMonitor {
private static final String TAG = "ContactsPackageMonitor";
private static final boolean VERBOSE_LOGGING = AbstractContactsProvider.VERBOSE_LOGGING;
private static final int BACKGROUND_TASK_PACKAGE_EVENT = 0;
private static ContactsPackageMonitor sInstance;
private Context mContext;
/** We run all BG tasks on this thread/handler sequentially. */
private final ContactsTaskScheduler mTaskScheduler;
private static class PackageEventArg {
final String packageName;
final PendingResult broadcastPendingResult;
private PackageEventArg(String packageName, PendingResult broadcastPendingResult) {
this.packageName = packageName;
this.broadcastPendingResult = broadcastPendingResult;
}
}
private ContactsPackageMonitor(Context context) {
mContext = context; // Can't use the app context due to a bug with shared process.
// Start the BG thread and register the receiver.
mTaskScheduler = new ContactsTaskScheduler(getClass().getSimpleName()) {
@Override
public void onPerformTask(int taskId, Object arg) {
switch (taskId) {
case BACKGROUND_TASK_PACKAGE_EVENT:
onPackageChanged((PackageEventArg) arg);
break;
}
}
};
}
private void start() {
if (VERBOSE_LOGGING) {
Log.v(TAG, "Starting... user="
+ android.os.Process.myUserHandle().getIdentifier());
}
registerReceiver();
}
public static synchronized void start(Context context) {
if (sInstance == null) {
sInstance = new ContactsPackageMonitor(context);
sInstance.start();
}
}
private void registerReceiver() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getData() == null) {
return; // Shouldn't happen.
}
final String changedPackage = intent.getData().getSchemeSpecificPart();
final PendingResult result = goAsync();
mTaskScheduler.scheduleTask(BACKGROUND_TASK_PACKAGE_EVENT,
new PackageEventArg(changedPackage, result));
}
}, filter);
}
private void onPackageChanged(PackageEventArg arg) {
try {
final String packageName = arg.packageName;
if (TextUtils.isEmpty(packageName)) {
Log.w(TAG, "Empty package name detected.");
return;
}
if (VERBOSE_LOGGING) Log.d(TAG, "onPackageChanged: Scanning package: " + packageName);
// First, tell CP2.
final ContactsProvider2 provider = getProvider(mContext, ContactsContract.AUTHORITY);
if (provider != null) {
provider.onPackageChanged(packageName);
}
// Next, if the package is gone, clean up the voicemail.
cleanupVoicemail(mContext, packageName);
} finally {
if (VERBOSE_LOGGING) Log.v(TAG, "Calling PendingResult.finish()...");
arg.broadcastPendingResult.finish();
}
}
@VisibleForTesting
static void cleanupVoicemail(Context context, String packageName) {
if (PackageUtils.isPackageInstalled(context, packageName)) {
return; // Still installed.
}
if (VERBOSE_LOGGING) Log.d(TAG, "Cleaning up data for package: " + packageName);
// Delete both voicemail content and voicemail status entries for this package.
final VoicemailContentProvider provider = getProvider(context, VoicemailContract.AUTHORITY);
if (provider != null) {
provider.removeBySourcePackage(packageName);
}
}
private static <T extends ContentProvider> T getProvider(Context context, String authority) {
final IContentProvider iprovider = context.getContentResolver().acquireProvider(authority);
final ContentProvider provider = ContentProvider.coerceToLocalContentProvider(iprovider);
if (provider != null) {
return (T) provider;
}
Slog.wtf(TAG, "Provider for " + authority + " not found");
return null;
}
}