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