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