Home | History | Annotate | Download | only in contacts
      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 
     39     public VoicemailNotifier(Context context, Uri baseUri) {
     40         mContext = context;
     41         mBaseUri = baseUri;
     42         mVoicemailPermissions = new VoicemailPermissions(mContext);
     43     }
     44 
     45     public void addIntentActions(String action) {
     46         mIntentActions.add(action);
     47     }
     48 
     49     public void addModifiedPackages(Collection<String> packages) {
     50         mModifiedPackages.addAll(packages);
     51     }
     52 
     53     public void addUri(Uri uri) {
     54         mUris.add(uri);
     55     }
     56 
     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      */
    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      */
    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