1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.homepage.contextualcards; 18 19 import android.content.ContentProvider; 20 import android.content.ContentResolver; 21 import android.content.ContentValues; 22 import android.content.UriMatcher; 23 import android.database.Cursor; 24 import android.database.sqlite.SQLiteDatabase; 25 import android.database.sqlite.SQLiteQueryBuilder; 26 import android.net.Uri; 27 import android.os.Build; 28 import android.os.StrictMode; 29 import android.util.Log; 30 31 import androidx.annotation.VisibleForTesting; 32 33 import com.android.settingslib.utils.ThreadUtils; 34 35 /** 36 * Provider stores and manages user interaction feedback for homepage contextual cards. 37 */ 38 public class CardContentProvider extends ContentProvider { 39 40 public static final String CARD_AUTHORITY = "com.android.settings.homepage.CardContentProvider"; 41 42 public static final Uri REFRESH_CARD_URI = new Uri.Builder() 43 .scheme(ContentResolver.SCHEME_CONTENT) 44 .authority(CardContentProvider.CARD_AUTHORITY) 45 .appendPath(CardDatabaseHelper.CARD_TABLE) 46 .build(); 47 48 public static final Uri DELETE_CARD_URI = new Uri.Builder() 49 .scheme(ContentResolver.SCHEME_CONTENT) 50 .authority(CardContentProvider.CARD_AUTHORITY) 51 .appendPath(CardDatabaseHelper.CardColumns.CARD_DISMISSED) 52 .build(); 53 54 private static final String TAG = "CardContentProvider"; 55 /** URI matcher for ContentProvider queries. */ 56 private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); 57 /** URI matcher type for cards table */ 58 private static final int MATCH_CARDS = 100; 59 60 static { URI_MATCHER.addURI(CARD_AUTHORITY, CardDatabaseHelper.CARD_TABLE, MATCH_CARDS)61 URI_MATCHER.addURI(CARD_AUTHORITY, CardDatabaseHelper.CARD_TABLE, MATCH_CARDS); 62 } 63 64 private CardDatabaseHelper mDBHelper; 65 66 @Override onCreate()67 public boolean onCreate() { 68 mDBHelper = CardDatabaseHelper.getInstance(getContext()); 69 return true; 70 } 71 72 @Override insert(Uri uri, ContentValues values)73 public Uri insert(Uri uri, ContentValues values) { 74 final ContentValues[] cvs = {values}; 75 bulkInsert(uri, cvs); 76 return uri; 77 } 78 79 @Override bulkInsert(Uri uri, ContentValues[] values)80 public int bulkInsert(Uri uri, ContentValues[] values) { 81 final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); 82 int numInserted = 0; 83 final SQLiteDatabase database = mDBHelper.getWritableDatabase(); 84 85 try { 86 maybeEnableStrictMode(); 87 88 final String table = getTableFromMatch(uri); 89 database.beginTransaction(); 90 91 // Here deletion first is avoiding redundant insertion. According to cl/215350754 92 database.delete(table, null /* whereClause */, null /* whereArgs */); 93 for (ContentValues value : values) { 94 long ret = database.insert(table, null /* nullColumnHack */, value); 95 if (ret != -1L) { 96 numInserted++; 97 } else { 98 Log.e(TAG, "The row " + value.getAsString(CardDatabaseHelper.CardColumns.NAME) 99 + " insertion failed! Please check your data."); 100 } 101 } 102 database.setTransactionSuccessful(); 103 getContext().getContentResolver().notifyChange(uri, null /* observer */); 104 } finally { 105 database.endTransaction(); 106 StrictMode.setThreadPolicy(oldPolicy); 107 } 108 return numInserted; 109 } 110 111 @Override delete(Uri uri, String selection, String[] selectionArgs)112 public int delete(Uri uri, String selection, String[] selectionArgs) { 113 throw new UnsupportedOperationException("delete operation not supported currently."); 114 } 115 116 @Override getType(Uri uri)117 public String getType(Uri uri) { 118 throw new UnsupportedOperationException("getType operation not supported currently."); 119 } 120 121 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)122 public Cursor query(Uri uri, String[] projection, String selection, 123 String[] selectionArgs, String sortOrder) { 124 final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); 125 try { 126 maybeEnableStrictMode(); 127 128 final SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); 129 final String table = getTableFromMatch(uri); 130 queryBuilder.setTables(table); 131 final SQLiteDatabase database = mDBHelper.getReadableDatabase(); 132 final Cursor cursor = queryBuilder.query(database, 133 projection, selection, selectionArgs, null /* groupBy */, null /* having */, 134 sortOrder); 135 136 cursor.setNotificationUri(getContext().getContentResolver(), uri); 137 return cursor; 138 } finally { 139 StrictMode.setThreadPolicy(oldPolicy); 140 } 141 } 142 143 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)144 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 145 throw new UnsupportedOperationException("update operation not supported currently."); 146 } 147 148 @VisibleForTesting maybeEnableStrictMode()149 void maybeEnableStrictMode() { 150 if (Build.IS_DEBUGGABLE && ThreadUtils.isMainThread()) { 151 enableStrictMode(); 152 } 153 } 154 155 @VisibleForTesting enableStrictMode()156 void enableStrictMode() { 157 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().build()); 158 } 159 160 @VisibleForTesting getTableFromMatch(Uri uri)161 String getTableFromMatch(Uri uri) { 162 final int match = URI_MATCHER.match(uri); 163 String table; 164 switch (match) { 165 case MATCH_CARDS: 166 table = CardDatabaseHelper.CARD_TABLE; 167 break; 168 default: 169 throw new IllegalArgumentException("Unknown Uri format: " + uri); 170 } 171 return table; 172 } 173 } 174