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