aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/providers/contacts/VoicemailNotifier.java
blob: 159cec7381511345e60f703c195d49d5c4e26294 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package com.android.providers.contacts;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Binder;
import android.provider.VoicemailContract;
import android.util.ArraySet;
import android.util.Log;

import com.google.android.collect.Lists;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

/**
 * Aggregates voicemail broadcasts from multiple operations in to a single one. The URIs will be
 * {@link VoicemailContract.Voicemails#DIR_TYPE} instead of {@link
 * VoicemailContract.Voicemails#ITEM_TYPE} if multiple URIs is notified.
 */
public class VoicemailNotifier {

    private final String TAG = "VoicemailNotifier";

    private final Context mContext;
    private final Uri mBaseUri;

    private final VoicemailPermissions mVoicemailPermissions;

    private final Set<String> mIntentActions = new ArraySet<>();
    private final Set<String> mModifiedPackages = new ArraySet<>();
    private final Set<Uri> mUris = new ArraySet<>();

    public VoicemailNotifier(Context context, Uri baseUri) {
        mContext = context;
        mBaseUri = baseUri;
        mVoicemailPermissions = new VoicemailPermissions(mContext);
    }

    public void addIntentActions(String action) {
        mIntentActions.add(action);
    }

    public void addModifiedPackages(Collection<String> packages) {
        mModifiedPackages.addAll(packages);
    }

    public void addUri(Uri uri) {
        mUris.add(uri);
    }

    public void sendNotification() {
        Uri uri = mUris.size() == 1 ? mUris.iterator().next() : mBaseUri;
        mContext.getContentResolver().notifyChange(uri, null, true);
        Collection<String> callingPackages = getCallingPackages();
        // Now fire individual intents.
        for (String intentAction : mIntentActions) {
            // self_change extra should be included only for provider_changed events.
            boolean includeSelfChangeExtra = intentAction.equals(Intent.ACTION_PROVIDER_CHANGED);
            Log.i(TAG, "receivers for " + intentAction + " :" + getBroadcastReceiverComponents(
                    intentAction, uri));
            for (ComponentName component :
                    getBroadcastReceiverComponents(intentAction, uri)) {
                boolean hasFullReadAccess =
                        mVoicemailPermissions.packageHasReadAccess(component.getPackageName());
                boolean hasOwnAccess =
                        mVoicemailPermissions.packageHasOwnVoicemailAccess(
                                component.getPackageName());
                // If we don't have full access, ignore the broadcast if the package isn't affected
                // by the change or doesn't have access to its own messages.
                if (!hasFullReadAccess
                        && (!mModifiedPackages.contains(component.getPackageName())
                                || !hasOwnAccess)) {
                    continue;
                }

                Intent intent = new Intent(intentAction, uri);
                intent.setComponent(component);
                if (includeSelfChangeExtra && callingPackages != null) {
                    intent.putExtra(VoicemailContract.EXTRA_SELF_CHANGE,
                            callingPackages.contains(component.getPackageName()));
                }
                mContext.sendBroadcast(intent);
                Log.v(TAG, String.format("Sent intent. act:%s, url:%s, comp:%s," +
                                " self_change:%s", intent.getAction(), intent.getData(),
                        component.getClassName(),
                        intent.hasExtra(VoicemailContract.EXTRA_SELF_CHANGE) ?
                                intent.getBooleanExtra(VoicemailContract.EXTRA_SELF_CHANGE, false) :
                                null));
            }
        }
        mIntentActions.clear();
        mModifiedPackages.clear();
        mUris.clear();
    }

    /**
     * Returns the package names of the calling process. If the calling process has more than
     * one packages, this returns them all
     */
    private Collection<String> getCallingPackages() {
        int caller = Binder.getCallingUid();
        if (caller == 0) {
            return null;
        }
        return Lists.newArrayList(mContext.getPackageManager().getPackagesForUid(caller));
    }

    /**
     * Determines the components that can possibly receive the specified intent.
     */
    private List<ComponentName> getBroadcastReceiverComponents(String intentAction, Uri uri) {
        Intent intent = new Intent(intentAction, uri);
        List<ComponentName> receiverComponents = new ArrayList<ComponentName>();
        // For broadcast receivers ResolveInfo.activityInfo is the one that is populated.
        for (ResolveInfo resolveInfo :
                mContext.getPackageManager().queryBroadcastReceivers(intent, 0)) {
            ActivityInfo activityInfo = resolveInfo.activityInfo;
            receiverComponents.add(new ComponentName(activityInfo.packageName, activityInfo.name));
        }
        return receiverComponents;
    }
}