PushManagementService.java

  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}