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;
}
}
|