PushManagementService.java

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