1package eu.siacs.conversations.services;
2
3import android.util.Log;
4
5import com.google.android.gms.common.ConnectionResult;
6import com.google.android.gms.common.GoogleApiAvailability;
7import com.google.firebase.iid.FirebaseInstanceId;
8import com.google.firebase.iid.InstanceIdResult;
9
10import eu.siacs.conversations.Config;
11import eu.siacs.conversations.R;
12import eu.siacs.conversations.entities.Account;
13import eu.siacs.conversations.entities.Conversation;
14import eu.siacs.conversations.utils.PhoneHelper;
15import eu.siacs.conversations.xml.Element;
16import eu.siacs.conversations.xml.Namespace;
17import eu.siacs.conversations.xmpp.OnIqPacketReceived;
18import eu.siacs.conversations.xmpp.XmppConnection;
19import eu.siacs.conversations.xmpp.forms.Data;
20import eu.siacs.conversations.xmpp.stanzas.IqPacket;
21import rocks.xmpp.addr.Jid;
22
23public class PushManagementService {
24
25 protected final XmppConnectionService mXmppConnectionService;
26
27 PushManagementService(XmppConnectionService service) {
28 this.mXmppConnectionService = service;
29 }
30
31 private static Data findResponseData(IqPacket response) {
32 final Element command = response.findChild("command", Namespace.COMMANDS);
33 final Element x = command == null ? null : command.findChild("x", Namespace.DATA);
34 return x == null ? null : Data.parse(x);
35 }
36
37 private Jid getAppServer() {
38 return Jid.of(mXmppConnectionService.getString(R.string.app_server));
39 }
40
41 void registerPushTokenOnServer(final Account account) {
42 Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": has push support");
43 retrieveFcmInstanceToken(token -> {
44 final String androidId = PhoneHelper.getAndroidId(mXmppConnectionService);
45 final IqPacket packet = mXmppConnectionService.getIqGenerator().pushTokenToAppServer(getAppServer(), token, androidId);
46 mXmppConnectionService.sendIqPacket(account, packet, (a, response) -> {
47 final Data data = findResponseData(response);
48 if (response.getType() == IqPacket.TYPE.RESULT && data != null) {
49 try {
50 String node = data.getValue("node");
51 String secret = data.getValue("secret");
52 Jid jid = Jid.of(data.getValue("jid"));
53 if (node != null && secret != null) {
54 enablePushOnServer(a, jid, node, secret);
55 }
56 } catch (IllegalArgumentException e) {
57 e.printStackTrace();
58 }
59 } else {
60 Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": invalid response from app server");
61 }
62 });
63 });
64 }
65
66 void registerPushTokenOnServer(final Conversation conversation) {
67 Log.d(Config.LOGTAG, conversation.getAccount().getJid().asBareJid() + ": room "+conversation.getJid().asBareJid()+" has push support");
68 retrieveFcmInstanceToken(token -> {
69 final Jid muc = conversation.getJid().asBareJid();
70 final String androidId = PhoneHelper.getAndroidId(mXmppConnectionService);
71 final IqPacket packet = mXmppConnectionService.getIqGenerator().pushTokenToAppServer(getAppServer(), token, androidId, muc);
72 packet.setTo(muc);
73 mXmppConnectionService.sendIqPacket(conversation.getAccount(), packet, (a, response) -> {
74 final Data data = findResponseData(response);
75 if (response.getType() == IqPacket.TYPE.RESULT && data != null) {
76 try {
77 final String node = data.getValue("node");
78 final String secret = data.getValue("secret");
79 final Jid jid = Jid.of(data.getValue("jid"));
80 if (node != null && secret != null) {
81 enablePushOnServer(conversation, jid, node, secret);
82 }
83 } catch (IllegalArgumentException e) {
84 e.printStackTrace();
85 }
86 } else {
87 Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": invalid response from app server");
88 }
89 });
90 });
91 }
92
93 private void enablePushOnServer(final Account account, final Jid appServer, final String node, final String secret) {
94 final IqPacket enable = mXmppConnectionService.getIqGenerator().enablePush(appServer, node, secret);
95 mXmppConnectionService.sendIqPacket(account, enable, (a, p) -> {
96 if (p.getType() == IqPacket.TYPE.RESULT) {
97 Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": successfully enabled push on server");
98 } else if (p.getType() == IqPacket.TYPE.ERROR) {
99 Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": enabling push on server failed");
100 }
101 });
102 }
103
104 private void enablePushOnServer(final Conversation conversation, final Jid appServer, final String node, final String secret) {
105 final Jid muc = conversation.getJid().asBareJid();
106 final IqPacket enable = mXmppConnectionService.getIqGenerator().enablePush(appServer, node, secret);
107 enable.setTo(muc);
108 mXmppConnectionService.sendIqPacket(conversation.getAccount(), enable, (a, p) -> {
109 if (p.getType() == IqPacket.TYPE.RESULT) {
110 Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": successfully enabled push on " + muc);
111 if (conversation.setAttribute(Conversation.ATTRIBUTE_ALWAYS_NOTIFY, node)) {
112 mXmppConnectionService.updateConversation(conversation);
113 }
114 } else if (p.getType() == IqPacket.TYPE.ERROR) {
115 Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": enabling push on " + muc + " failed");
116 }
117 });
118 }
119
120 public void disablePushOnServer(final Conversation conversation) {
121 final Jid muc = conversation.getJid().asBareJid();
122 final String node = conversation.getAttribute(Conversation.ATTRIBUTE_PUSH_NODE);
123 if (node != null) {
124 final IqPacket disable = mXmppConnectionService.getIqGenerator().disablePush(getAppServer(), node);
125 disable.setTo(muc);
126 mXmppConnectionService.sendIqPacket(conversation.getAccount(), disable, (account, response) -> {
127 if (response.getType() == IqPacket.TYPE.ERROR) {
128 Log.d(Config.LOGTAG,account.getJid().asBareJid()+": unable to disable push for room "+muc);
129 }
130 });
131 } else {
132 Log.d(Config.LOGTAG,conversation.getAccount().getJid().asBareJid()+": room "+muc+" has no stored node. unable to disable push");
133 }
134 }
135
136 private void retrieveFcmInstanceToken(final OnGcmInstanceTokenRetrieved instanceTokenRetrieved) {
137 FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(task -> {
138 if (!task.isSuccessful()) {
139 Log.d(Config.LOGTAG, "unable to get Firebase instance token", task.getException());
140 }
141 final InstanceIdResult result = task.getResult();
142 if (result != null) {
143 instanceTokenRetrieved.onGcmInstanceTokenRetrieved(result.getToken());
144 }
145 });
146
147 }
148
149
150 public boolean available(Account account) {
151 final XmppConnection connection = account.getXmppConnection();
152 return connection != null
153 && connection.getFeatures().sm()
154 && connection.getFeatures().push()
155 && playServicesAvailable();
156 }
157
158 private boolean playServicesAvailable() {
159 return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(mXmppConnectionService) == ConnectionResult.SUCCESS;
160 }
161
162 public boolean isStub() {
163 return false;
164 }
165
166 interface OnGcmInstanceTokenRetrieved {
167 void onGcmInstanceTokenRetrieved(String token);
168 }
169}