UnifiedPushDatabase.java

  1package eu.siacs.conversations.persistance;
  2
  3import android.content.ContentValues;
  4import android.content.Context;
  5import android.database.Cursor;
  6import android.database.sqlite.SQLiteDatabase;
  7import android.database.sqlite.SQLiteOpenHelper;
  8import android.util.Log;
  9
 10import androidx.annotation.Nullable;
 11
 12import com.google.common.base.MoreObjects;
 13import com.google.common.base.Objects;
 14import com.google.common.base.Optional;
 15import com.google.common.collect.ImmutableList;
 16
 17import org.jetbrains.annotations.NotNull;
 18
 19import java.util.List;
 20
 21import eu.siacs.conversations.Config;
 22import eu.siacs.conversations.services.UnifiedPushBroker;
 23
 24public class UnifiedPushDatabase extends SQLiteOpenHelper {
 25    private static final String DATABASE_NAME = "unified-push-distributor";
 26    private static final int DATABASE_VERSION = 1;
 27
 28    private static UnifiedPushDatabase instance;
 29
 30    public static UnifiedPushDatabase getInstance(final Context context) {
 31        synchronized (UnifiedPushDatabase.class) {
 32            if (instance == null) {
 33                instance = new UnifiedPushDatabase(context.getApplicationContext());
 34            }
 35            return instance;
 36        }
 37    }
 38
 39    private UnifiedPushDatabase(@Nullable Context context) {
 40        super(context, DATABASE_NAME, null, DATABASE_VERSION);
 41    }
 42
 43    @Override
 44    public void onCreate(final SQLiteDatabase sqLiteDatabase) {
 45        sqLiteDatabase.execSQL(
 46                "CREATE TABLE push (account TEXT, transport TEXT, application TEXT NOT NULL, instance TEXT NOT NULL UNIQUE, endpoint TEXT, expiration NUMBER DEFAULT 0)");
 47    }
 48
 49    public boolean register(final String application, final String instance) {
 50        final SQLiteDatabase sqLiteDatabase = getWritableDatabase();
 51        sqLiteDatabase.beginTransaction();
 52        final Optional<String> existingApplication;
 53        try (final Cursor cursor =
 54                sqLiteDatabase.query(
 55                        "push",
 56                        new String[] {"application"},
 57                        "instance=?",
 58                        new String[] {instance},
 59                        null,
 60                        null,
 61                        null)) {
 62            if (cursor != null && cursor.moveToFirst()) {
 63                existingApplication = Optional.of(cursor.getString(0));
 64            } else {
 65                existingApplication = Optional.absent();
 66            }
 67        }
 68        if (existingApplication.isPresent()) {
 69            sqLiteDatabase.setTransactionSuccessful();
 70            sqLiteDatabase.endTransaction();
 71            return application.equals(existingApplication.get());
 72        }
 73        final ContentValues contentValues = new ContentValues();
 74        contentValues.put("application", application);
 75        contentValues.put("instance", instance);
 76        contentValues.put("expiration", 0);
 77        final long inserted = sqLiteDatabase.insert("push", null, contentValues);
 78        if (inserted > 0) {
 79            Log.d(Config.LOGTAG, "inserted new application/instance tuple into unified push db");
 80        }
 81        sqLiteDatabase.setTransactionSuccessful();
 82        sqLiteDatabase.endTransaction();
 83        return true;
 84    }
 85
 86    public List<PushTarget> getRenewals(final String account, final String transport) {
 87        final ImmutableList.Builder<PushTarget> renewalBuilder = ImmutableList.builder();
 88        final long expiration = System.currentTimeMillis() + UnifiedPushBroker.TIME_TO_RENEW;
 89        final SQLiteDatabase sqLiteDatabase = getReadableDatabase();
 90        try (final Cursor cursor =
 91                sqLiteDatabase.query(
 92                        "push",
 93                        new String[] {"application", "instance"},
 94                        "account <> ? OR transport <> ? OR expiration < " + expiration,
 95                        new String[] {account, transport},
 96                        null,
 97                        null,
 98                        null)) {
 99            while (cursor != null && cursor.moveToNext()) {
100                renewalBuilder.add(
101                        new PushTarget(
102                                cursor.getString(cursor.getColumnIndexOrThrow("application")),
103                                cursor.getString(cursor.getColumnIndexOrThrow("instance"))));
104            }
105        }
106        return renewalBuilder.build();
107    }
108
109    public ApplicationEndpoint getEndpoint(
110            final String account, final String transport, final String instance) {
111        final long expiration = System.currentTimeMillis() + UnifiedPushBroker.TIME_TO_RENEW;
112        final SQLiteDatabase sqLiteDatabase = getReadableDatabase();
113        try (final Cursor cursor =
114                sqLiteDatabase.query(
115                        "push",
116                        new String[] {"application", "endpoint"},
117                        "account = ? AND transport = ? AND instance = ? AND endpoint IS NOT NULL AND expiration >= "
118                                + expiration,
119                        new String[] {account, transport, instance},
120                        null,
121                        null,
122                        null)) {
123            if (cursor != null && cursor.moveToFirst()) {
124                return new ApplicationEndpoint(
125                        cursor.getString(cursor.getColumnIndexOrThrow("application")),
126                        cursor.getString(cursor.getColumnIndexOrThrow("endpoint")));
127            }
128        }
129        return null;
130    }
131
132    @Override
133    public void onUpgrade(
134            final SQLiteDatabase sqLiteDatabase, final int oldVersion, final int newVersion) {}
135
136    public boolean updateEndpoint(
137            final String instance,
138            final String account,
139            final String transport,
140            final String endpoint,
141            final long expiration) {
142        final SQLiteDatabase sqLiteDatabase = getWritableDatabase();
143        sqLiteDatabase.beginTransaction();
144        final String existingEndpoint;
145        try (final Cursor cursor =
146                sqLiteDatabase.query(
147                        "push",
148                        new String[] {"endpoint"},
149                        "instance=?",
150                        new String[] {instance},
151                        null,
152                        null,
153                        null)) {
154            if (cursor != null && cursor.moveToFirst()) {
155                existingEndpoint = cursor.getString(0);
156            } else {
157                existingEndpoint = null;
158            }
159        }
160        final ContentValues contentValues = new ContentValues();
161        contentValues.put("account", account);
162        contentValues.put("transport", transport);
163        contentValues.put("endpoint", endpoint);
164        contentValues.put("expiration", expiration);
165        sqLiteDatabase.update("push", contentValues, "instance=?", new String[] {instance});
166        sqLiteDatabase.setTransactionSuccessful();
167        sqLiteDatabase.endTransaction();
168        return !endpoint.equals(existingEndpoint);
169    }
170
171    public List<PushTarget> getPushTargets(final String account, final String transport) {
172        final ImmutableList.Builder<PushTarget> renewalBuilder = ImmutableList.builder();
173        final SQLiteDatabase sqLiteDatabase = getReadableDatabase();
174        try (final Cursor cursor =
175                sqLiteDatabase.query(
176                        "push",
177                        new String[] {"application", "instance"},
178                        "account = ?",
179                        new String[] {account},
180                        null,
181                        null,
182                        null)) {
183            while (cursor != null && cursor.moveToNext()) {
184                renewalBuilder.add(
185                        new PushTarget(
186                                cursor.getString(cursor.getColumnIndexOrThrow("application")),
187                                cursor.getString(cursor.getColumnIndexOrThrow("instance"))));
188            }
189        }
190        return renewalBuilder.build();
191    }
192
193    public boolean deleteInstance(final String instance) {
194        final SQLiteDatabase sqLiteDatabase = getReadableDatabase();
195        final int rows = sqLiteDatabase.delete("push", "instance=?", new String[] {instance});
196        return rows >= 1;
197    }
198
199    public boolean deleteApplication(final String application) {
200        final SQLiteDatabase sqLiteDatabase = getReadableDatabase();
201        final int rows = sqLiteDatabase.delete("push", "application=?", new String[] {application});
202        return rows >= 1;
203    }
204
205    public static class ApplicationEndpoint {
206        public final String application;
207        public final String endpoint;
208
209        public ApplicationEndpoint(String application, String endpoint) {
210            this.application = application;
211            this.endpoint = endpoint;
212        }
213    }
214
215    public static class PushTarget {
216        public final String application;
217        public final String instance;
218
219        public PushTarget(final String application, final String instance) {
220            this.application = application;
221            this.instance = instance;
222        }
223
224        @NotNull
225        @Override
226        public String toString() {
227            return MoreObjects.toStringHelper(this)
228                    .add("application", application)
229                    .add("instance", instance)
230                    .toString();
231        }
232
233        @Override
234        public boolean equals(Object o) {
235            if (this == o) return true;
236            if (o == null || getClass() != o.getClass()) return false;
237            PushTarget that = (PushTarget) o;
238            return Objects.equal(application, that.application)
239                    && Objects.equal(instance, that.instance);
240        }
241
242        @Override
243        public int hashCode() {
244            return Objects.hashCode(application, instance);
245        }
246    }
247}