1package eu.siacs.conversations.services;
2
3import android.content.BroadcastReceiver;
4import android.content.Context;
5import android.content.Intent;
6import android.content.pm.ResolveInfo;
7import android.net.Uri;
8import android.util.Log;
9
10import com.google.common.base.Charsets;
11import com.google.common.base.Joiner;
12import com.google.common.base.Strings;
13import com.google.common.collect.Lists;
14import com.google.common.hash.Hashing;
15import com.google.common.io.BaseEncoding;
16
17import java.util.Arrays;
18import java.util.Collection;
19import java.util.List;
20
21import eu.siacs.conversations.Config;
22import eu.siacs.conversations.persistance.UnifiedPushDatabase;
23import eu.siacs.conversations.utils.Compatibility;
24
25public class UnifiedPushDistributor extends BroadcastReceiver {
26
27 public static final String ACTION_REGISTER = "org.unifiedpush.android.distributor.REGISTER";
28 public static final String ACTION_UNREGISTER = "org.unifiedpush.android.distributor.UNREGISTER";
29 public static final String ACTION_BYTE_MESSAGE =
30 "org.unifiedpush.android.distributor.feature.BYTES_MESSAGE";
31 public static final String ACTION_REGISTRATION_FAILED =
32 "org.unifiedpush.android.connector.REGISTRATION_FAILED";
33 public static final String ACTION_MESSAGE = "org.unifiedpush.android.connector.MESSAGE";
34 public static final String ACTION_NEW_ENDPOINT =
35 "org.unifiedpush.android.connector.NEW_ENDPOINT";
36
37 public static final String PREFERENCE_ACCOUNT = "up_push_account";
38 public static final String PREFERENCE_PUSH_SERVER = "up_push_server";
39
40 public static final List<String> PREFERENCES =
41 Arrays.asList(PREFERENCE_ACCOUNT, PREFERENCE_PUSH_SERVER);
42
43 @Override
44 public void onReceive(final Context context, final Intent intent) {
45 if (intent == null) {
46 return;
47 }
48 final String action = intent.getAction();
49 final String application = intent.getStringExtra("application");
50 final String instance = intent.getStringExtra("token");
51 final List<String> features = intent.getStringArrayListExtra("features");
52 switch (Strings.nullToEmpty(action)) {
53 case ACTION_REGISTER:
54 register(context, application, instance, features);
55 break;
56 case ACTION_UNREGISTER:
57 unregister(context, instance);
58 break;
59 case Intent.ACTION_PACKAGE_FULLY_REMOVED:
60 unregisterApplication(context, intent.getData());
61 break;
62 default:
63 Log.d(Config.LOGTAG, "UnifiedPushDistributor received unknown action " + action);
64 break;
65 }
66 }
67
68 private void register(
69 final Context context,
70 final String application,
71 final String instance,
72 final Collection<String> features) {
73 if (Strings.isNullOrEmpty(application) || Strings.isNullOrEmpty(instance)) {
74 Log.w(Config.LOGTAG, "ignoring invalid UnifiedPush registration");
75 return;
76 }
77 final List<String> receivers = getBroadcastReceivers(context, application);
78 if (receivers.contains(application)) {
79 final boolean byteMessage = features != null && features.contains(ACTION_BYTE_MESSAGE);
80 Log.d(
81 Config.LOGTAG,
82 "received up registration from "
83 + application
84 + "/"
85 + instance
86 + " features: "
87 + features);
88 if (UnifiedPushDatabase.getInstance(context).register(application, instance)) {
89 Log.d(
90 Config.LOGTAG,
91 "successfully created UnifiedPush entry. waking up XmppConnectionService");
92 final Intent serviceIntent = new Intent(context, XmppConnectionService.class);
93 serviceIntent.setAction(XmppConnectionService.ACTION_RENEW_UNIFIED_PUSH_ENDPOINTS);
94 serviceIntent.putExtra("instance", instance);
95 Compatibility.startService(context, serviceIntent);
96 } else {
97 Log.d(Config.LOGTAG, "not successful. sending error message back to application");
98 final Intent registrationFailed = new Intent(ACTION_REGISTRATION_FAILED);
99 registrationFailed.setPackage(application);
100 registrationFailed.putExtra("token", instance);
101 context.sendBroadcast(registrationFailed);
102 }
103 } else {
104 Log.d(
105 Config.LOGTAG,
106 "ignoring invalid UnifiedPush registration. Unknown application "
107 + application);
108 }
109 }
110
111 private List<String> getBroadcastReceivers(final Context context, final String application) {
112 final Intent messageIntent = new Intent(ACTION_MESSAGE);
113 messageIntent.setPackage(application);
114 final List<ResolveInfo> resolveInfo =
115 context.getPackageManager().queryBroadcastReceivers(messageIntent, 0);
116 return Lists.transform(
117 resolveInfo, ri -> ri.activityInfo == null ? null : ri.activityInfo.packageName);
118 }
119
120 private void unregister(final Context context, final String instance) {
121 if (Strings.isNullOrEmpty(instance)) {
122 Log.w(Config.LOGTAG, "ignoring invalid UnifiedPush un-registration");
123 return;
124 }
125 final UnifiedPushDatabase unifiedPushDatabase = UnifiedPushDatabase.getInstance(context);
126 if (unifiedPushDatabase.deleteInstance(instance)) {
127 Log.d(Config.LOGTAG, "successfully removed " + instance + " from UnifiedPush");
128 }
129 }
130
131 private void unregisterApplication(final Context context, final Uri uri) {
132 if (uri != null && "package".equalsIgnoreCase(uri.getScheme())) {
133 final String application = uri.getSchemeSpecificPart();
134 if (Strings.isNullOrEmpty(application)) {
135 return;
136 }
137 Log.d(Config.LOGTAG, "app " + application + " has been removed from the system");
138 final UnifiedPushDatabase database = UnifiedPushDatabase.getInstance(context);
139 if (database.deleteApplication(application)) {
140 Log.d(Config.LOGTAG, "successfully removed " + application + " from UnifiedPush");
141 }
142 }
143 }
144
145 public static String hash(String... components) {
146 return BaseEncoding.base64()
147 .encode(
148 Hashing.sha256()
149 .hashString(Joiner.on('\0').join(components), Charsets.UTF_8)
150 .asBytes());
151 }
152}