1/*
2 * Copyright (C) 2011 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
17package com.android.providers.contacts;
18
19import static com.android.providers.contacts.util.DbQueryUtils.concatenateClauses;
20
21import android.content.ContentUris;
22import android.content.ContentValues;
23import android.content.Context;
24import android.database.Cursor;
25import android.database.sqlite.SQLiteDatabase;
26import android.database.sqlite.SQLiteQueryBuilder;
27import android.net.Uri;
28import android.os.ParcelFileDescriptor;
29import android.provider.VoicemailContract.Status;
30import android.util.ArraySet;
31
32import com.android.common.content.ProjectionMap;
33import com.android.providers.contacts.VoicemailContentProvider.UriData;
34
35/**
36 * Implementation of {@link VoicemailTable.Delegate} for the voicemail status table.
37 *
38 * Public methods of this class are thread-safe as it is used in a content provider, which should
39 * be thread-safe.
40 */
41public class VoicemailStatusTable implements VoicemailTable.Delegate {
42
43    private static final ProjectionMap sStatusProjectionMap = new ProjectionMap.Builder()
44            .add(Status._ID)
45            .add(Status.PHONE_ACCOUNT_COMPONENT_NAME)
46            .add(Status.PHONE_ACCOUNT_ID)
47            .add(Status.CONFIGURATION_STATE)
48            .add(Status.DATA_CHANNEL_STATE)
49            .add(Status.NOTIFICATION_CHANNEL_STATE)
50            .add(Status.SETTINGS_URI)
51            .add(Status.SOURCE_PACKAGE)
52            .add(Status.VOICEMAIL_ACCESS_URI)
53            .add(Status.QUOTA_OCCUPIED)
54            .add(Status.QUOTA_TOTAL)
55            .add(Status.SOURCE_TYPE)
56            .build();
57
58    private static final Object DATABASE_LOCK = new Object();
59
60    private final String mTableName;
61    private final Context mContext;
62    private final CallLogDatabaseHelper mDbHelper;
63    private final VoicemailTable.DelegateHelper mDelegateHelper;
64
65    public VoicemailStatusTable(String tableName, Context context, CallLogDatabaseHelper dbHelper,
66            VoicemailTable.DelegateHelper delegateHelper) {
67        mTableName = tableName;
68        mContext = context;
69        mDbHelper = dbHelper;
70        mDelegateHelper = delegateHelper;
71    }
72
73    @Override
74    public Uri insert(UriData uriData, ContentValues values) {
75        synchronized (DATABASE_LOCK) {
76            SQLiteDatabase db = mDbHelper.getWritableDatabase();
77            // Try to update before insert.
78            String combinedClause = uriData.getWhereClause();
79            int rowsChanged = createDatabaseModifier(db)
80                    .update(uriData.getUri(), mTableName, values, combinedClause, null);
81            if (rowsChanged != 0) {
82                final String[] selection = new String[] {Status._ID};
83                Cursor c = db.query(mTableName, selection, combinedClause, null, null, null, null);
84                c.moveToFirst();
85                int rowId = c.getInt(0);
86                c.close();
87                return ContentUris.withAppendedId(uriData.getUri(), rowId);
88            }
89            ContentValues copiedValues = new ContentValues(values);
90            mDelegateHelper.checkAndAddSourcePackageIntoValues(uriData, copiedValues);
91            long rowId = createDatabaseModifier(db).insert(mTableName, null, copiedValues);
92            if (rowId > 0) {
93                return ContentUris.withAppendedId(uriData.getUri(), rowId);
94            } else {
95                return null;
96            }
97        }
98    }
99
100    @Override
101    public int bulkInsert(UriData uriData, ContentValues[] values) {
102        int count = 0;
103        for (ContentValues value : values) {
104            Uri uri = insert(uriData, value);
105            if (uri != null) {
106                count++;
107            }
108        }
109        return count;
110    }
111
112    @Override
113    public int delete(UriData uriData, String selection, String[] selectionArgs) {
114        synchronized (DATABASE_LOCK) {
115            SQLiteDatabase db = mDbHelper.getWritableDatabase();
116            String combinedClause = concatenateClauses(selection, uriData.getWhereClause());
117            return createDatabaseModifier(db).delete(mTableName, combinedClause,
118                    selectionArgs);
119        }
120    }
121
122    @Override
123    public Cursor query(UriData uriData, String[] projection, String selection,
124            String[] selectionArgs, String sortOrder) {
125        synchronized (DATABASE_LOCK) {
126            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
127            qb.setTables(mTableName);
128            qb.setProjectionMap(sStatusProjectionMap);
129            qb.setStrict(true);
130
131            String combinedClause = concatenateClauses(selection, uriData.getWhereClause());
132            SQLiteDatabase db = mDbHelper.getReadableDatabase();
133            Cursor c = qb
134                    .query(db, projection, combinedClause, selectionArgs, null, null, sortOrder);
135            if (c != null) {
136                c.setNotificationUri(mContext.getContentResolver(), Status.CONTENT_URI);
137            }
138            return c;
139        }
140    }
141
142    @Override
143    public int update(UriData uriData, ContentValues values, String selection,
144            String[] selectionArgs) {
145        synchronized (DATABASE_LOCK) {
146            SQLiteDatabase db = mDbHelper.getWritableDatabase();
147            String combinedClause = concatenateClauses(selection, uriData.getWhereClause());
148            return createDatabaseModifier(db)
149                    .update(uriData.getUri(), mTableName, values, combinedClause, selectionArgs);
150        }
151    }
152
153    @Override
154    public String getType(UriData uriData) {
155        if (uriData.hasId()) {
156            return Status.ITEM_TYPE;
157        } else {
158            return Status.DIR_TYPE;
159        }
160    }
161
162    @Override
163    public ParcelFileDescriptor openFile(UriData uriData, String mode) {
164        throw new UnsupportedOperationException("File operation is not supported for status table");
165    }
166
167    private DatabaseModifier createDatabaseModifier(SQLiteDatabase db) {
168        return new DbModifierWithNotification(mTableName, db, mContext);
169    }
170
171    @Override
172    public ArraySet<String> getSourcePackages() {
173        return mDbHelper.selectDistinctColumn(mTableName, Status.SOURCE_PACKAGE);
174    }
175}
176