1 package com.android.providers.contacts; 2 3 import android.content.ComponentName; 4 import android.content.Context; 5 import android.content.Intent; 6 import android.content.pm.ActivityInfo; 7 import android.content.pm.ResolveInfo; 8 import android.net.Uri; 9 import android.os.Binder; 10 import android.provider.VoicemailContract; 11 import android.util.ArraySet; 12 import android.util.Log; 13 14 import com.google.android.collect.Lists; 15 16 import java.util.ArrayList; 17 import java.util.Collection; 18 import java.util.List; 19 import java.util.Set; 20 21 /** 22 * Aggregates voicemail broadcasts from multiple operations in to a single one. The URIs will be 23 * {@link VoicemailContract.Voicemails#DIR_TYPE} instead of {@link 24 * VoicemailContract.Voicemails#ITEM_TYPE} if multiple URIs is notified. 25 */ 26 public class VoicemailNotifier { 27 28 private final String TAG = "VoicemailNotifier"; 29 30 private final Context mContext; 31 private final Uri mBaseUri; 32 33 private final VoicemailPermissions mVoicemailPermissions; 34 35 private final Set<String> mIntentActions = new ArraySet<>(); 36 private final Set<String> mModifiedPackages = new ArraySet<>(); 37 private final Set<Uri> mUris = new ArraySet<>(); 38 VoicemailNotifier(Context context, Uri baseUri)39 public VoicemailNotifier(Context context, Uri baseUri) { 40 mContext = context; 41 mBaseUri = baseUri; 42 mVoicemailPermissions = new VoicemailPermissions(mContext); 43 } 44 addIntentActions(String action)45 public void addIntentActions(String action) { 46 mIntentActions.add(action); 47 } 48 addModifiedPackages(Collection<String> packages)49 public void addModifiedPackages(Collection<String> packages) { 50 mModifiedPackages.addAll(packages); 51 } 52 addUri(Uri uri)53 public void addUri(Uri uri) { 54 mUris.add(uri); 55 } 56 sendNotification()57 public void sendNotification() { 58 Uri uri = mUris.size() == 1 ? mUris.iterator().next() : mBaseUri; 59 mContext.getContentResolver().notifyChange(uri, null, true); 60 Collection<String> callingPackages = getCallingPackages(); 61 // Now fire individual intents. 62 for (String intentAction : mIntentActions) { 63 // self_change extra should be included only for provider_changed events. 64 boolean includeSelfChangeExtra = intentAction.equals(Intent.ACTION_PROVIDER_CHANGED); 65 Log.i(TAG, "receivers for " + intentAction + " :" + getBroadcastReceiverComponents( 66 intentAction, uri)); 67 for (ComponentName component : 68 getBroadcastReceiverComponents(intentAction, uri)) { 69 boolean hasFullReadAccess = 70 mVoicemailPermissions.packageHasReadAccess(component.getPackageName()); 71 boolean hasOwnAccess = 72 mVoicemailPermissions.packageHasOwnVoicemailAccess( 73 component.getPackageName()); 74 // If we don't have full access, ignore the broadcast if the package isn't affected 75 // by the change or doesn't have access to its own messages. 76 if (!hasFullReadAccess 77 && (!mModifiedPackages.contains(component.getPackageName()) 78 || !hasOwnAccess)) { 79 continue; 80 } 81 82 Intent intent = new Intent(intentAction, uri); 83 intent.setComponent(component); 84 if (includeSelfChangeExtra && callingPackages != null) { 85 intent.putExtra(VoicemailContract.EXTRA_SELF_CHANGE, 86 callingPackages.contains(component.getPackageName())); 87 } 88 mContext.sendBroadcast(intent); 89 Log.v(TAG, String.format("Sent intent. act:%s, url:%s, comp:%s," + 90 " self_change:%s", intent.getAction(), intent.getData(), 91 component.getClassName(), 92 intent.hasExtra(VoicemailContract.EXTRA_SELF_CHANGE) ? 93 intent.getBooleanExtra(VoicemailContract.EXTRA_SELF_CHANGE, false) : 94 null)); 95 } 96 } 97 mIntentActions.clear(); 98 mModifiedPackages.clear(); 99 mUris.clear(); 100 } 101 102 /** 103 * Returns the package names of the calling process. If the calling process has more than 104 * one packages, this returns them all 105 */ getCallingPackages()106 private Collection<String> getCallingPackages() { 107 int caller = Binder.getCallingUid(); 108 if (caller == 0) { 109 return null; 110 } 111 return Lists.newArrayList(mContext.getPackageManager().getPackagesForUid(caller)); 112 } 113 114 /** 115 * Determines the components that can possibly receive the specified intent. 116 */ getBroadcastReceiverComponents(String intentAction, Uri uri)117 private List<ComponentName> getBroadcastReceiverComponents(String intentAction, Uri uri) { 118 Intent intent = new Intent(intentAction, uri); 119 List<ComponentName> receiverComponents = new ArrayList<ComponentName>(); 120 // For broadcast receivers ResolveInfo.activityInfo is the one that is populated. 121 for (ResolveInfo resolveInfo : 122 mContext.getPackageManager().queryBroadcastReceivers(intent, 0)) { 123 ActivityInfo activityInfo = resolveInfo.activityInfo; 124 receiverComponents.add(new ComponentName(activityInfo.packageName, activityInfo.name)); 125 } 126 return receiverComponents; 127 } 128 } 129