1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 package com.android.providers.contacts; 17 18 import android.content.BroadcastReceiver; 19 import android.content.BroadcastReceiver.PendingResult; 20 import android.content.ContentProvider; 21 import android.content.Context; 22 import android.content.IContentProvider; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.provider.ContactsContract; 26 import android.provider.VoicemailContract; 27 import android.text.TextUtils; 28 import android.util.Log; 29 import android.util.Slog; 30 31 import com.android.providers.contacts.util.PackageUtils; 32 33 import com.google.common.annotations.VisibleForTesting; 34 35 /** 36 * - Handles package related broadcasts. 37 * - Also scan changed packages while the process wasn't running using PM.getChangedPackages(). 38 */ 39 public class ContactsPackageMonitor { 40 private static final String TAG = "ContactsPackageMonitor"; 41 42 private static final boolean VERBOSE_LOGGING = AbstractContactsProvider.VERBOSE_LOGGING; 43 44 private static final int BACKGROUND_TASK_PACKAGE_EVENT = 0; 45 46 private static ContactsPackageMonitor sInstance; 47 48 private Context mContext; 49 50 /** We run all BG tasks on this thread/handler sequentially. */ 51 private final ContactsTaskScheduler mTaskScheduler; 52 53 private static class PackageEventArg { 54 final String packageName; 55 final PendingResult broadcastPendingResult; 56 PackageEventArg(String packageName, PendingResult broadcastPendingResult)57 private PackageEventArg(String packageName, PendingResult broadcastPendingResult) { 58 this.packageName = packageName; 59 this.broadcastPendingResult = broadcastPendingResult; 60 } 61 } 62 ContactsPackageMonitor(Context context)63 private ContactsPackageMonitor(Context context) { 64 mContext = context; // Can't use the app context due to a bug with shared process. 65 66 // Start the BG thread and register the receiver. 67 mTaskScheduler = new ContactsTaskScheduler(getClass().getSimpleName()) { 68 @Override 69 public void onPerformTask(int taskId, Object arg) { 70 switch (taskId) { 71 case BACKGROUND_TASK_PACKAGE_EVENT: 72 onPackageChanged((PackageEventArg) arg); 73 break; 74 } 75 } 76 }; 77 } 78 start()79 private void start() { 80 if (VERBOSE_LOGGING) { 81 Log.v(TAG, "Starting... user=" 82 + android.os.Process.myUserHandle().getIdentifier()); 83 } 84 85 registerReceiver(); 86 } 87 start(Context context)88 public static synchronized void start(Context context) { 89 if (sInstance == null) { 90 sInstance = new ContactsPackageMonitor(context); 91 sInstance.start(); 92 } 93 } 94 registerReceiver()95 private void registerReceiver() { 96 final IntentFilter filter = new IntentFilter(); 97 98 filter.addAction(Intent.ACTION_PACKAGE_UNSTOPPED); 99 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 100 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 101 filter.addAction(Intent.ACTION_PACKAGE_REPLACED); 102 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 103 filter.addDataScheme("package"); 104 105 mContext.registerReceiver(new BroadcastReceiver() { 106 @Override 107 public void onReceive(Context context, Intent intent) { 108 if (intent.getData() == null) { 109 return; // Shouldn't happen. 110 } 111 final String changedPackage = intent.getData().getSchemeSpecificPart(); 112 final PendingResult result = goAsync(); 113 114 mTaskScheduler.scheduleTask(BACKGROUND_TASK_PACKAGE_EVENT, 115 new PackageEventArg(changedPackage, result)); 116 } 117 }, filter); 118 } 119 onPackageChanged(PackageEventArg arg)120 private void onPackageChanged(PackageEventArg arg) { 121 try { 122 final String packageName = arg.packageName; 123 if (TextUtils.isEmpty(packageName)) { 124 Log.w(TAG, "Empty package name detected."); 125 return; 126 } 127 if (VERBOSE_LOGGING) Log.d(TAG, "onPackageChanged: Scanning package: " + packageName); 128 129 // First, tell CP2. 130 final ContactsProvider2 provider = getProvider(mContext, ContactsContract.AUTHORITY); 131 if (provider != null) { 132 provider.onPackageChanged(packageName); 133 } 134 135 // Next, if the package is gone, clean up the voicemail. 136 cleanupVoicemail(mContext, packageName); 137 } finally { 138 if (VERBOSE_LOGGING) Log.v(TAG, "Calling PendingResult.finish()..."); 139 arg.broadcastPendingResult.finish(); 140 } 141 } 142 143 @VisibleForTesting cleanupVoicemail(Context context, String packageName)144 static void cleanupVoicemail(Context context, String packageName) { 145 if (PackageUtils.isPackageInstalled(context, packageName)) { 146 return; // Still installed. 147 } 148 if (VERBOSE_LOGGING) Log.d(TAG, "Cleaning up data for package: " + packageName); 149 150 // Delete both voicemail content and voicemail status entries for this package. 151 final VoicemailContentProvider provider = getProvider(context, VoicemailContract.AUTHORITY); 152 if (provider != null) { 153 provider.removeBySourcePackage(packageName); 154 } 155 } 156 getProvider(Context context, String authority)157 private static <T extends ContentProvider> T getProvider(Context context, String authority) { 158 final IContentProvider iprovider = context.getContentResolver().acquireProvider(authority); 159 final ContentProvider provider = ContentProvider.coerceToLocalContentProvider(iprovider); 160 if (provider != null) { 161 return (T) provider; 162 } 163 Slog.wtf(TAG, "Provider for " + authority + " not found"); 164 return null; 165 } 166 } 167