/* * Copyright (C) 2013-2017 microG Project Team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.microg.gms.gcm; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.text.TextUtils; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class GcmDatabase extends SQLiteOpenHelper { private static final String TAG = GcmDatabase.class.getSimpleName(); public static final String DB_NAME = "gcmstatus"; private static int DB_VERSION = 1; private static final String CREATE_TABLE_APPS = "CREATE TABLE apps (" + "package_name TEXT," + "last_error TEXT DEFAULT ''," + "last_message_timestamp INTEGER," + "total_message_count INTEGER," + "total_message_bytes INTEGER," + "allow_register INTEGER DEFAULT 1," + "wake_for_delivery INTEGER DEFAULT 1," + "PRIMARY KEY (package_name));"; private static final String TABLE_APPS = "apps"; private static final String FIELD_PACKAGE_NAME = "package_name"; private static final String FIELD_LAST_ERROR = "last_error"; private static final String FIELD_LAST_MESSAGE_TIMESTAMP = "last_message_timestamp"; private static final String FIELD_TOTAL_MESSAGE_COUNT = "total_message_count"; private static final String FIELD_TOTAL_MESSAGE_BYTES = "total_message_bytes"; private static final String FIELD_ALLOW_REGISTER = "allow_register"; private static final String FIELD_WAKE_FOR_DELIVERY = "wake_for_delivery"; private static final String CREATE_TABLE_REGISTRATIONS = "CREATE TABLE registrations (" + "package_name TEXT," + "signature TEXT," + "timestamp INTEGER," + "register_id TEXT," + "PRIMARY KEY (package_name, signature));"; private static final String TABLE_REGISTRATIONS = "registrations"; private static final String FIELD_SIGNATURE = "signature"; private static final String FIELD_TIMESTAMP = "timestamp"; private static final String FIELD_REGISTER_ID = "register_id"; private Context context; public GcmDatabase(Context context) { super(context, DB_NAME, null, DB_VERSION); this.context = context; } public static class App { public final String packageName; public final String lastError; public final long lastMessageTimestamp; public final long totalMessageCount; public final long totalMessageBytes; public final boolean allowRegister; public final boolean wakeForDelivery; private App(Cursor cursor) { packageName = cursor.getString(cursor.getColumnIndex(FIELD_PACKAGE_NAME)); lastError = cursor.getString(cursor.getColumnIndex(FIELD_LAST_ERROR)); lastMessageTimestamp = cursor.getLong(cursor.getColumnIndex(FIELD_LAST_MESSAGE_TIMESTAMP)); totalMessageCount = cursor.getLong(cursor.getColumnIndex(FIELD_TOTAL_MESSAGE_COUNT)); totalMessageBytes = cursor.getLong(cursor.getColumnIndex(FIELD_TOTAL_MESSAGE_BYTES)); allowRegister = cursor.getLong(cursor.getColumnIndex(FIELD_ALLOW_REGISTER)) == 1; wakeForDelivery = cursor.getLong(cursor.getColumnIndex(FIELD_WAKE_FOR_DELIVERY)) == 1; } public boolean hasError() { return !TextUtils.isEmpty(lastError); } } public static class Registration { public final String packageName; public final String signature; public final long timestamp; public final String registerId; public Registration(Cursor cursor) { packageName = cursor.getString(cursor.getColumnIndex(FIELD_PACKAGE_NAME)); signature = cursor.getString(cursor.getColumnIndex(FIELD_SIGNATURE)); timestamp = cursor.getLong(cursor.getColumnIndex(FIELD_TIMESTAMP)); registerId = cursor.getString(cursor.getColumnIndex(FIELD_REGISTER_ID)); } } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE_APPS); db.execSQL(CREATE_TABLE_REGISTRATIONS); importLegacyData(db); } public synchronized List getAppList() { SQLiteDatabase db = getReadableDatabase(); Cursor cursor = db.query(TABLE_APPS, null, null, null, null, null, null); if (cursor != null) { List result = new ArrayList<>(); while (cursor.moveToNext()) { result.add(new App(cursor)); } cursor.close(); return result; } return Collections.emptyList(); } public synchronized List getRegistrationList() { SQLiteDatabase db = getReadableDatabase(); Cursor cursor = db.query(TABLE_REGISTRATIONS, null, null, null, null, null, null); if (cursor != null) { List result = new ArrayList<>(); while (cursor.moveToNext()) { result.add(new Registration(cursor)); } cursor.close(); return result; } return Collections.emptyList(); } public synchronized List getRegistrationsByApp(String packageName) { SQLiteDatabase db = getReadableDatabase(); Cursor cursor = db.query(TABLE_REGISTRATIONS, null, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}, null, null, null); if (cursor != null) { List result = new ArrayList<>(); while (cursor.moveToNext()) { result.add(new Registration(cursor)); } cursor.close(); return result; } return Collections.emptyList(); } public synchronized void setAppAllowRegister(String packageName, boolean allowRegister) { SQLiteDatabase db = getWritableDatabase(); ContentValues cv = new ContentValues(); cv.put(FIELD_ALLOW_REGISTER, allowRegister ? 1 : 0); db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}); } public synchronized void setAppWakeForDelivery(String packageName, boolean wakeForDelivery) { SQLiteDatabase db = getWritableDatabase(); ContentValues cv = new ContentValues(); cv.put(FIELD_WAKE_FOR_DELIVERY, wakeForDelivery ? 1 : 0); db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}); } public synchronized void removeApp(String packageName) { SQLiteDatabase db = getWritableDatabase(); db.delete(TABLE_REGISTRATIONS, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}); db.delete(TABLE_APPS, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}); } public synchronized void noteAppRegistrationError(String packageName, String error) { SQLiteDatabase db = getWritableDatabase(); ContentValues cv = new ContentValues(); cv.put(FIELD_LAST_ERROR, error); db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}); } public synchronized void noteAppKnown(String packageName, boolean allowRegister) { SQLiteDatabase db = getWritableDatabase(); db.beginTransaction(); App app = getApp(db, packageName); ContentValues cv = new ContentValues(); cv.put(FIELD_ALLOW_REGISTER, allowRegister); if (app == null) { cv.put(FIELD_PACKAGE_NAME, packageName); db.insert(TABLE_APPS, null, cv); } else { db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}); } db.setTransactionSuccessful(); db.endTransaction(); } public synchronized void noteAppMessage(String packageName, int numBytes) { SQLiteDatabase db = getWritableDatabase(); db.beginTransaction(); App app = getApp(db, packageName); ContentValues cv = new ContentValues(); cv.put(FIELD_LAST_MESSAGE_TIMESTAMP, System.currentTimeMillis()); if (app == null) { cv.put(FIELD_PACKAGE_NAME, packageName); cv.put(FIELD_TOTAL_MESSAGE_COUNT, 1); cv.put(FIELD_TOTAL_MESSAGE_BYTES, numBytes); db.insert(TABLE_APPS, null, cv); } else { cv.put(FIELD_TOTAL_MESSAGE_COUNT, app.totalMessageCount + 1); cv.put(FIELD_TOTAL_MESSAGE_BYTES, app.totalMessageBytes + numBytes); db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}); } db.setTransactionSuccessful(); db.endTransaction(); } public synchronized void noteAppRegistered(String packageName, String signature, String registrationId) { SQLiteDatabase db = getWritableDatabase(); db.beginTransaction(); App app = getApp(db, packageName); if (app == null) { ContentValues cv = new ContentValues(); cv.put(FIELD_PACKAGE_NAME, packageName); db.insert(TABLE_APPS, null, cv); } else { ContentValues cv = new ContentValues(); cv.put(FIELD_LAST_ERROR, ""); db.update(TABLE_APPS, cv, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}); } ContentValues cv = new ContentValues(); cv.put(FIELD_PACKAGE_NAME, packageName); cv.put(FIELD_SIGNATURE, signature); cv.put(FIELD_REGISTER_ID, registrationId); cv.put(FIELD_TIMESTAMP, System.currentTimeMillis()); db.insertWithOnConflict(TABLE_REGISTRATIONS, null, cv, SQLiteDatabase.CONFLICT_REPLACE); db.setTransactionSuccessful(); db.endTransaction(); } public synchronized void noteAppUnregistered(String packageName, String signature) { SQLiteDatabase db = getWritableDatabase(); db.delete(TABLE_REGISTRATIONS, FIELD_PACKAGE_NAME + " LIKE ? AND " + FIELD_SIGNATURE + " LIKE ?", new String[]{packageName, signature}); } public App getApp(String packageName) { return getApp(getReadableDatabase(), packageName); } private App getApp(SQLiteDatabase db, String packageName) { Cursor cursor = db.query(TABLE_APPS, null, FIELD_PACKAGE_NAME + " LIKE ?", new String[]{packageName}, null, null, null, "1"); if (cursor != null) { try { if (cursor.moveToNext()) { return new App(cursor); } } finally { cursor.close(); } } return null; } public Registration getRegistration(String packageName, String signature) { return getRegistration(getReadableDatabase(), packageName, signature); } private Registration getRegistration(SQLiteDatabase db, String packageName, String signature) { Cursor cursor = db.query(TABLE_REGISTRATIONS, null, FIELD_PACKAGE_NAME + " LIKE ? AND " + FIELD_SIGNATURE + " LIKE ?", new String[]{packageName, signature}, null, null, null, "1"); if (cursor != null) { try { if (cursor.moveToNext()) { return new Registration(cursor); } } finally { cursor.close(); } } return null; } @SuppressWarnings("deprecation") private void importLegacyData(SQLiteDatabase db) { db.beginTransaction(); GcmLegacyData legacyData = new GcmLegacyData(context); for (GcmLegacyData.LegacyAppInfo appInfo : legacyData.getAppsInfo()) { ContentValues cv = new ContentValues(); cv.put(FIELD_PACKAGE_NAME, appInfo.app); cv.put(FIELD_TOTAL_MESSAGE_COUNT, legacyData.getAppMessageCount(appInfo.app)); cv.put(FIELD_LAST_ERROR, appInfo.hasUnregistrationError() ? "Unregistration error" : null); db.insert(TABLE_APPS, null, cv); cv.clear(); if (appInfo.isRegistered()) { cv.put(FIELD_PACKAGE_NAME, appInfo.app); cv.put(FIELD_SIGNATURE, appInfo.appSignature); cv.put(FIELD_REGISTER_ID, appInfo.registerID); db.insert(TABLE_REGISTRATIONS, null, cv); } } db.setTransactionSuccessful(); db.endTransaction(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { throw new IllegalStateException("Upgrades not supported"); } }