Account.java

  1package eu.siacs.conversations.entities;
  2
  3import android.content.ContentValues;
  4import android.database.Cursor;
  5import android.os.SystemClock;
  6import android.util.Log;
  7
  8import com.google.common.base.Strings;
  9
 10import org.json.JSONException;
 11import org.json.JSONObject;
 12
 13import java.util.ArrayList;
 14import java.util.Collection;
 15import java.util.HashMap;
 16import java.util.HashSet;
 17import java.util.List;
 18import java.util.Map;
 19import java.util.Set;
 20import java.util.concurrent.CopyOnWriteArraySet;
 21
 22import eu.siacs.conversations.Config;
 23import eu.siacs.conversations.R;
 24import eu.siacs.conversations.crypto.PgpDecryptionService;
 25import eu.siacs.conversations.crypto.axolotl.AxolotlService;
 26import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
 27import eu.siacs.conversations.services.AvatarService;
 28import eu.siacs.conversations.services.XmppConnectionService;
 29import eu.siacs.conversations.utils.UIHelper;
 30import eu.siacs.conversations.utils.XmppUri;
 31import eu.siacs.conversations.xmpp.Jid;
 32import eu.siacs.conversations.xmpp.XmppConnection;
 33import eu.siacs.conversations.xmpp.jingle.RtpCapability;
 34
 35public class Account extends AbstractEntity implements AvatarService.Avatarable {
 36
 37    public static final String TABLENAME = "accounts";
 38
 39    public static final String USERNAME = "username";
 40    public static final String SERVER = "server";
 41    public static final String PASSWORD = "password";
 42    public static final String OPTIONS = "options";
 43    public static final String ROSTERVERSION = "rosterversion";
 44    public static final String KEYS = "keys";
 45    public static final String AVATAR = "avatar";
 46    public static final String DISPLAY_NAME = "display_name";
 47    public static final String HOSTNAME = "hostname";
 48    public static final String PORT = "port";
 49    public static final String STATUS = "status";
 50    public static final String STATUS_MESSAGE = "status_message";
 51    public static final String RESOURCE = "resource";
 52
 53    public static final String PINNED_MECHANISM_KEY = "pinned_mechanism";
 54    public static final String PRE_AUTH_REGISTRATION_TOKEN = "pre_auth_registration";
 55
 56    public static final int OPTION_USETLS = 0;
 57    public static final int OPTION_DISABLED = 1;
 58    public static final int OPTION_REGISTER = 2;
 59    public static final int OPTION_USECOMPRESSION = 3;
 60    public static final int OPTION_MAGIC_CREATE = 4;
 61    public static final int OPTION_REQUIRES_ACCESS_MODE_CHANGE = 5;
 62    public static final int OPTION_LOGGED_IN_SUCCESSFULLY = 6;
 63    public static final int OPTION_HTTP_UPLOAD_AVAILABLE = 7;
 64    public static final int OPTION_UNVERIFIED = 8;
 65    public static final int OPTION_FIXED_USERNAME = 9;
 66    private static final String KEY_PGP_SIGNATURE = "pgp_signature";
 67    private static final String KEY_PGP_ID = "pgp_id";
 68    protected final JSONObject keys;
 69    private final Roster roster = new Roster(this);
 70    private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
 71    public final Set<Conversation> pendingConferenceJoins = new HashSet<>();
 72    public final Set<Conversation> pendingConferenceLeaves = new HashSet<>();
 73    public final Set<Conversation> inProgressConferenceJoins = new HashSet<>();
 74    public final Set<Conversation> inProgressConferencePings = new HashSet<>();
 75    protected Jid jid;
 76    protected String password;
 77    protected int options = 0;
 78    protected State status = State.OFFLINE;
 79    private State lastErrorStatus = State.OFFLINE;
 80    protected String resource;
 81    protected String avatar;
 82    protected String hostname = null;
 83    protected int port = 5222;
 84    protected boolean online = false;
 85    private String rosterVersion;
 86    private String displayName = null;
 87    private AxolotlService axolotlService = null;
 88    private PgpDecryptionService pgpDecryptionService = null;
 89    private XmppConnection xmppConnection = null;
 90    private long mEndGracePeriod = 0L;
 91    private final Map<Jid, Bookmark> bookmarks = new HashMap<>();
 92    private boolean bookmarksLoaded = false;
 93    private Presence.Status presenceStatus = Presence.Status.ONLINE;
 94    private String presenceStatusMessage = null;
 95
 96    public Account(final Jid jid, final String password) {
 97        this(java.util.UUID.randomUUID().toString(), jid,
 98                password, 0, null, "", null, null, null, 5222, Presence.Status.ONLINE, null);
 99    }
100
101    private Account(final String uuid, final Jid jid,
102                    final String password, final int options, final String rosterVersion, final String keys,
103                    final String avatar, String displayName, String hostname, int port,
104                    final Presence.Status status, String statusMessage) {
105        this.uuid = uuid;
106        this.jid = jid;
107        this.password = password;
108        this.options = options;
109        this.rosterVersion = rosterVersion;
110        JSONObject tmp;
111        try {
112            tmp = new JSONObject(keys);
113        } catch (JSONException e) {
114            tmp = new JSONObject();
115        }
116        this.keys = tmp;
117        this.avatar = avatar;
118        this.displayName = displayName;
119        this.hostname = hostname;
120        this.port = port;
121        this.presenceStatus = status;
122        this.presenceStatusMessage = statusMessage;
123    }
124
125    public static Account fromCursor(final Cursor cursor) {
126        final Jid jid;
127        try {
128            String resource = cursor.getString(cursor.getColumnIndex(RESOURCE));
129            jid = Jid.of(
130                    cursor.getString(cursor.getColumnIndex(USERNAME)),
131                    cursor.getString(cursor.getColumnIndex(SERVER)),
132                    resource == null || resource.trim().isEmpty() ? null : resource);
133        } catch (final IllegalArgumentException ignored) {
134            Log.d(Config.LOGTAG, cursor.getString(cursor.getColumnIndex(USERNAME)) + "@" + cursor.getString(cursor.getColumnIndex(SERVER)));
135            throw new AssertionError(ignored);
136        }
137        return new Account(cursor.getString(cursor.getColumnIndex(UUID)),
138                jid,
139                cursor.getString(cursor.getColumnIndex(PASSWORD)),
140                cursor.getInt(cursor.getColumnIndex(OPTIONS)),
141                cursor.getString(cursor.getColumnIndex(ROSTERVERSION)),
142                cursor.getString(cursor.getColumnIndex(KEYS)),
143                cursor.getString(cursor.getColumnIndex(AVATAR)),
144                cursor.getString(cursor.getColumnIndex(DISPLAY_NAME)),
145                cursor.getString(cursor.getColumnIndex(HOSTNAME)),
146                cursor.getInt(cursor.getColumnIndex(PORT)),
147                Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndex(STATUS))),
148                cursor.getString(cursor.getColumnIndex(STATUS_MESSAGE)));
149    }
150
151    public boolean httpUploadAvailable(long filesize) {
152        return xmppConnection != null && xmppConnection.getFeatures().httpUpload(filesize);
153    }
154
155    public boolean httpUploadAvailable() {
156        return isOptionSet(OPTION_HTTP_UPLOAD_AVAILABLE) || httpUploadAvailable(0);
157    }
158
159    public String getDisplayName() {
160        return displayName;
161    }
162
163    public void setDisplayName(String displayName) {
164        this.displayName = displayName;
165    }
166
167    public XmppConnection.Identity getServerIdentity() {
168        if (xmppConnection == null) {
169            return XmppConnection.Identity.UNKNOWN;
170        } else {
171            return xmppConnection.getServerIdentity();
172        }
173    }
174
175    public Contact getSelfContact() {
176        return getRoster().getContact(jid);
177    }
178
179    public boolean hasPendingPgpIntent(Conversation conversation) {
180        return pgpDecryptionService != null && pgpDecryptionService.hasPendingIntent(conversation);
181    }
182
183    public boolean isPgpDecryptionServiceConnected() {
184        return pgpDecryptionService != null && pgpDecryptionService.isConnected();
185    }
186
187    public boolean setShowErrorNotification(boolean newValue) {
188        boolean oldValue = showErrorNotification();
189        setKey("show_error", Boolean.toString(newValue));
190        return newValue != oldValue;
191    }
192
193    public boolean showErrorNotification() {
194        String key = getKey("show_error");
195        return key == null || Boolean.parseBoolean(key);
196    }
197
198    public boolean isEnabled() {
199        return !isOptionSet(Account.OPTION_DISABLED);
200    }
201
202    public boolean isOptionSet(final int option) {
203        return ((options & (1 << option)) != 0);
204    }
205
206    public boolean setOption(final int option, final boolean value) {
207        final int before = this.options;
208        if (value) {
209            this.options |= 1 << option;
210        } else {
211            this.options &= ~(1 << option);
212        }
213        return before != this.options;
214    }
215
216    public String getUsername() {
217        return jid.getEscapedLocal();
218    }
219
220    public boolean setJid(final Jid next) {
221        final Jid previousFull = this.jid;
222        final Jid prev = this.jid != null ? this.jid.asBareJid() : null;
223        final boolean changed = prev == null || (next != null && !prev.equals(next.asBareJid()));
224        if (changed) {
225            final AxolotlService oldAxolotlService = this.axolotlService;
226            if (oldAxolotlService != null) {
227                oldAxolotlService.destroy();
228                this.jid = next;
229                this.axolotlService = oldAxolotlService.makeNew();
230            }
231        }
232        this.jid = next;
233        return next != null && !next.equals(previousFull);
234    }
235
236    public Jid getDomain() {
237        return jid.getDomain();
238    }
239
240    public String getServer() {
241        return jid.getDomain().toEscapedString();
242    }
243
244    public String getPassword() {
245        return password;
246    }
247
248    public void setPassword(final String password) {
249        this.password = password;
250    }
251
252    public String getHostname() {
253        return Strings.nullToEmpty(this.hostname);
254    }
255
256    public void setHostname(String hostname) {
257        this.hostname = hostname;
258    }
259
260    public boolean isOnion() {
261        final String server = getServer();
262        return server != null && server.endsWith(".onion");
263    }
264
265    public int getPort() {
266        return this.port;
267    }
268
269    public void setPort(int port) {
270        this.port = port;
271    }
272
273    public State getStatus() {
274        if (isOptionSet(OPTION_DISABLED)) {
275            return State.DISABLED;
276        } else {
277            return this.status;
278        }
279    }
280
281    public State getLastErrorStatus() {
282        return this.lastErrorStatus;
283    }
284
285    public void setStatus(final State status) {
286        this.status = status;
287        if (status.isError || status == State.ONLINE) {
288            this.lastErrorStatus = status;
289        }
290    }
291
292    public State getTrueStatus() {
293        return this.status;
294    }
295
296    public boolean errorStatus() {
297        return getStatus().isError();
298    }
299
300    public boolean hasErrorStatus() {
301        return getXmppConnection() != null
302                && (getStatus().isError() || getStatus() == State.CONNECTING)
303                && getXmppConnection().getAttempt() >= 3;
304    }
305
306    public Presence.Status getPresenceStatus() {
307        return this.presenceStatus;
308    }
309
310    public void setPresenceStatus(Presence.Status status) {
311        this.presenceStatus = status;
312    }
313
314    public String getPresenceStatusMessage() {
315        return this.presenceStatusMessage;
316    }
317
318    public void setPresenceStatusMessage(String message) {
319        this.presenceStatusMessage = message;
320    }
321
322    public String getResource() {
323        return jid.getResource();
324    }
325
326    public void setResource(final String resource) {
327        this.jid = this.jid.withResource(resource);
328    }
329
330    public Jid getJid() {
331        return jid;
332    }
333
334    public JSONObject getKeys() {
335        return keys;
336    }
337
338    public String getKey(final String name) {
339        synchronized (this.keys) {
340            return this.keys.optString(name, null);
341        }
342    }
343
344    public int getKeyAsInt(final String name, int defaultValue) {
345        String key = getKey(name);
346        try {
347            return key == null ? defaultValue : Integer.parseInt(key);
348        } catch (NumberFormatException e) {
349            return defaultValue;
350        }
351    }
352
353    public boolean setKey(final String keyName, final String keyValue) {
354        synchronized (this.keys) {
355            try {
356                this.keys.put(keyName, keyValue);
357                return true;
358            } catch (final JSONException e) {
359                return false;
360            }
361        }
362    }
363
364    public boolean setPrivateKeyAlias(String alias) {
365        return setKey("private_key_alias", alias);
366    }
367
368    public String getPrivateKeyAlias() {
369        return getKey("private_key_alias");
370    }
371
372    @Override
373    public ContentValues getContentValues() {
374        final ContentValues values = new ContentValues();
375        values.put(UUID, uuid);
376        values.put(USERNAME, jid.getLocal());
377        values.put(SERVER, jid.getDomain().toEscapedString());
378        values.put(PASSWORD, password);
379        values.put(OPTIONS, options);
380        synchronized (this.keys) {
381            values.put(KEYS, this.keys.toString());
382        }
383        values.put(ROSTERVERSION, rosterVersion);
384        values.put(AVATAR, avatar);
385        values.put(DISPLAY_NAME, displayName);
386        values.put(HOSTNAME, hostname);
387        values.put(PORT, port);
388        values.put(STATUS, presenceStatus.toShowString());
389        values.put(STATUS_MESSAGE, presenceStatusMessage);
390        values.put(RESOURCE, jid.getResource());
391        return values;
392    }
393
394    public AxolotlService getAxolotlService() {
395        return axolotlService;
396    }
397
398    public void initAccountServices(final XmppConnectionService context) {
399        this.axolotlService = new AxolotlService(this, context);
400        this.pgpDecryptionService = new PgpDecryptionService(context);
401        if (xmppConnection != null) {
402            xmppConnection.addOnAdvancedStreamFeaturesAvailableListener(axolotlService);
403        }
404    }
405
406    public PgpDecryptionService getPgpDecryptionService() {
407        return this.pgpDecryptionService;
408    }
409
410    public XmppConnection getXmppConnection() {
411        return this.xmppConnection;
412    }
413
414    public void setXmppConnection(final XmppConnection connection) {
415        this.xmppConnection = connection;
416    }
417
418    public String getRosterVersion() {
419        if (this.rosterVersion == null) {
420            return "";
421        } else {
422            return this.rosterVersion;
423        }
424    }
425
426    public void setRosterVersion(final String version) {
427        this.rosterVersion = version;
428    }
429
430    public int countPresences() {
431        return this.getSelfContact().getPresences().size();
432    }
433
434    public int activeDevicesWithRtpCapability() {
435        int i = 0;
436        for(Presence presence : getSelfContact().getPresences().getPresences()) {
437            if (RtpCapability.check(presence) != RtpCapability.Capability.NONE) {
438                i++;
439            }
440        }
441        return i;
442    }
443
444    public String getPgpSignature() {
445        return getKey(KEY_PGP_SIGNATURE);
446    }
447
448    public boolean setPgpSignature(String signature) {
449        return setKey(KEY_PGP_SIGNATURE, signature);
450    }
451
452    public boolean unsetPgpSignature() {
453        synchronized (this.keys) {
454            return keys.remove(KEY_PGP_SIGNATURE) != null;
455        }
456    }
457
458    public long getPgpId() {
459        synchronized (this.keys) {
460            if (keys.has(KEY_PGP_ID)) {
461                try {
462                    return keys.getLong(KEY_PGP_ID);
463                } catch (JSONException e) {
464                    return 0;
465                }
466            } else {
467                return 0;
468            }
469        }
470    }
471
472    public boolean setPgpSignId(long pgpID) {
473        synchronized (this.keys) {
474            try {
475                if (pgpID == 0) {
476                    keys.remove(KEY_PGP_ID);
477                } else {
478                    keys.put(KEY_PGP_ID, pgpID);
479                }
480            } catch (JSONException e) {
481                return false;
482            }
483            return true;
484        }
485    }
486
487    public Roster getRoster() {
488        return this.roster;
489    }
490
491    public Collection<Bookmark> getBookmarks() {
492        synchronized (this.bookmarks) {
493            return new HashSet<>(this.bookmarks.values());
494        }
495    }
496
497    public boolean areBookmarksLoaded() { return bookmarksLoaded; }
498
499    public void setBookmarks(Map<Jid, Bookmark> bookmarks) {
500        synchronized (this.bookmarks) {
501            this.bookmarks.clear();
502            this.bookmarks.putAll(bookmarks);
503            this.bookmarksLoaded = true;
504        }
505    }
506
507    public void putBookmark(Bookmark bookmark) {
508        synchronized (this.bookmarks) {
509            this.bookmarks.put(bookmark.getJid(), bookmark);
510        }
511    }
512
513    public void removeBookmark(Bookmark bookmark) {
514        synchronized (this.bookmarks) {
515            this.bookmarks.remove(bookmark.getJid());
516        }
517    }
518
519    public void removeBookmark(Jid jid) {
520        synchronized (this.bookmarks) {
521            this.bookmarks.remove(jid);
522        }
523    }
524
525    public Set<Jid> getBookmarkedJids() {
526        synchronized (this.bookmarks) {
527            return new HashSet<>(this.bookmarks.keySet());
528        }
529    }
530
531    public Bookmark getBookmark(final Jid jid) {
532        synchronized (this.bookmarks) {
533            return this.bookmarks.get(jid.asBareJid());
534        }
535    }
536
537    public boolean setAvatar(final String filename) {
538        if (this.avatar != null && this.avatar.equals(filename)) {
539            return false;
540        } else {
541            this.avatar = filename;
542            return true;
543        }
544    }
545
546    public String getAvatar() {
547        return this.avatar;
548    }
549
550    public void activateGracePeriod(final long duration) {
551        if (duration > 0) {
552            this.mEndGracePeriod = SystemClock.elapsedRealtime() + duration;
553        }
554    }
555
556    public void deactivateGracePeriod() {
557        this.mEndGracePeriod = 0L;
558    }
559
560    public boolean inGracePeriod() {
561        return SystemClock.elapsedRealtime() < this.mEndGracePeriod;
562    }
563
564    public String getShareableUri() {
565        List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
566        String uri = "xmpp:" + this.getJid().asBareJid().toEscapedString();
567        if (fingerprints.size() > 0) {
568            return XmppUri.getFingerprintUri(uri, fingerprints, ';');
569        } else {
570            return uri;
571        }
572    }
573
574    public String getShareableLink() {
575        List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
576        String uri = "https://conversations.im/i/" + XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString());
577        if (fingerprints.size() > 0) {
578            return XmppUri.getFingerprintUri(uri, fingerprints, '&');
579        } else {
580            return uri;
581        }
582    }
583
584    private List<XmppUri.Fingerprint> getFingerprints() {
585        ArrayList<XmppUri.Fingerprint> fingerprints = new ArrayList<>();
586        if (axolotlService == null) {
587            return fingerprints;
588        }
589        fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO, axolotlService.getOwnFingerprint().substring(2), axolotlService.getOwnDeviceId()));
590        for (XmppAxolotlSession session : axolotlService.findOwnSessions()) {
591            if (session.getTrust().isVerified() && session.getTrust().isActive()) {
592                fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO, session.getFingerprint().substring(2).replaceAll("\\s", ""), session.getRemoteAddress().getDeviceId()));
593            }
594        }
595        return fingerprints;
596    }
597
598    public boolean isBlocked(final ListItem contact) {
599        final Jid jid = contact.getJid();
600        return jid != null && (blocklist.contains(jid.asBareJid()) || blocklist.contains(jid.getDomain()));
601    }
602
603    public boolean isBlocked(final Jid jid) {
604        return jid != null && blocklist.contains(jid.asBareJid());
605    }
606
607    public Collection<Jid> getBlocklist() {
608        return this.blocklist;
609    }
610
611    public void clearBlocklist() {
612        getBlocklist().clear();
613    }
614
615    public boolean isOnlineAndConnected() {
616        return this.getStatus() == State.ONLINE && this.getXmppConnection() != null;
617    }
618
619    @Override
620    public int getAvatarBackgroundColor() {
621        return UIHelper.getColorForName(jid.asBareJid().toString());
622    }
623
624    @Override
625    public String getAvatarName() {
626        throw new IllegalStateException("This method should not be called");
627    }
628
629    public enum State {
630        DISABLED(false, false),
631        OFFLINE(false),
632        CONNECTING(false),
633        ONLINE(false),
634        NO_INTERNET(false),
635        UNAUTHORIZED,
636        TEMPORARY_AUTH_FAILURE,
637        SERVER_NOT_FOUND,
638        REGISTRATION_SUCCESSFUL(false),
639        REGISTRATION_FAILED(true, false),
640        REGISTRATION_WEB(true, false),
641        REGISTRATION_CONFLICT(true, false),
642        REGISTRATION_NOT_SUPPORTED(true, false),
643        REGISTRATION_PLEASE_WAIT(true, false),
644        REGISTRATION_INVALID_TOKEN(true,false),
645        REGISTRATION_PASSWORD_TOO_WEAK(true, false),
646        TLS_ERROR,
647        TLS_ERROR_DOMAIN,
648        INCOMPATIBLE_SERVER,
649        TOR_NOT_AVAILABLE,
650        DOWNGRADE_ATTACK,
651        SESSION_FAILURE,
652        BIND_FAILURE,
653        HOST_UNKNOWN,
654        STREAM_ERROR,
655        STREAM_OPENING_ERROR,
656        POLICY_VIOLATION,
657        PAYMENT_REQUIRED,
658        MISSING_INTERNET_PERMISSION(false);
659
660        private final boolean isError;
661        private final boolean attemptReconnect;
662
663        State(final boolean isError) {
664            this(isError, true);
665        }
666
667        State(final boolean isError, final boolean reconnect) {
668            this.isError = isError;
669            this.attemptReconnect = reconnect;
670        }
671
672        State() {
673            this(true, true);
674        }
675
676        public boolean isError() {
677            return this.isError;
678        }
679
680        public boolean isAttemptReconnect() {
681            return this.attemptReconnect;
682        }
683
684        public int getReadableId() {
685            switch (this) {
686                case DISABLED:
687                    return R.string.account_status_disabled;
688                case ONLINE:
689                    return R.string.account_status_online;
690                case CONNECTING:
691                    return R.string.account_status_connecting;
692                case OFFLINE:
693                    return R.string.account_status_offline;
694                case UNAUTHORIZED:
695                    return R.string.account_status_unauthorized;
696                case SERVER_NOT_FOUND:
697                    return R.string.account_status_not_found;
698                case NO_INTERNET:
699                    return R.string.account_status_no_internet;
700                case REGISTRATION_FAILED:
701                    return R.string.account_status_regis_fail;
702                case REGISTRATION_WEB:
703                    return R.string.account_status_regis_web;
704                case REGISTRATION_CONFLICT:
705                    return R.string.account_status_regis_conflict;
706                case REGISTRATION_SUCCESSFUL:
707                    return R.string.account_status_regis_success;
708                case REGISTRATION_NOT_SUPPORTED:
709                    return R.string.account_status_regis_not_sup;
710                case REGISTRATION_INVALID_TOKEN:
711                    return R.string.account_status_regis_invalid_token;
712                case TLS_ERROR:
713                    return R.string.account_status_tls_error;
714                case TLS_ERROR_DOMAIN:
715                    return R.string.account_status_tls_error_domain;
716                case INCOMPATIBLE_SERVER:
717                    return R.string.account_status_incompatible_server;
718                case TOR_NOT_AVAILABLE:
719                    return R.string.account_status_tor_unavailable;
720                case BIND_FAILURE:
721                    return R.string.account_status_bind_failure;
722                case SESSION_FAILURE:
723                    return R.string.session_failure;
724                case DOWNGRADE_ATTACK:
725                    return R.string.sasl_downgrade;
726                case HOST_UNKNOWN:
727                    return R.string.account_status_host_unknown;
728                case POLICY_VIOLATION:
729                    return R.string.account_status_policy_violation;
730                case REGISTRATION_PLEASE_WAIT:
731                    return R.string.registration_please_wait;
732                case REGISTRATION_PASSWORD_TOO_WEAK:
733                    return R.string.registration_password_too_weak;
734                case STREAM_ERROR:
735                    return R.string.account_status_stream_error;
736                case STREAM_OPENING_ERROR:
737                    return R.string.account_status_stream_opening_error;
738                case PAYMENT_REQUIRED:
739                    return R.string.payment_required;
740                case MISSING_INTERNET_PERMISSION:
741                    return R.string.missing_internet_permission;
742                case TEMPORARY_AUTH_FAILURE:
743                    return R.string.account_status_temporary_auth_failure;
744                default:
745                    return R.string.account_status_unknown;
746            }
747        }
748    }
749}