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 */
16package com.android.providers.contacts;
17
18import android.content.ContentValues;
19import android.content.Context;
20import android.content.Intent;
21import android.content.res.AssetFileDescriptor;
22import android.database.Cursor;
23import android.database.sqlite.SQLiteDatabase;
24import android.net.Uri;
25import android.os.CancellationSignal;
26import android.provider.ContactsContract.Intents;
27
28import java.io.FileNotFoundException;
29import java.util.Locale;
30
31/**
32 * Simple content provider to handle directing profile-specific calls against a separate
33 * database from the rest of contacts.
34 */
35public class ProfileProvider extends AbstractContactsProvider {
36    private static final String READ_PERMISSION = "android.permission.READ_PROFILE";
37    private static final String WRITE_PERMISSION = "android.permission.WRITE_PROFILE";
38
39    // The Contacts provider handles most of the logic - this provider is only invoked when the
40    // URI belongs to a profile action, setting up the proper database.
41    private final ContactsProvider2 mDelegate;
42
43    public ProfileProvider(ContactsProvider2 delegate) {
44        mDelegate = delegate;
45    }
46
47    /**
48     * Performs a permission check on the read profile permission.  Checks the delegate contacts
49     * provider to see whether this is an authorized one-time-use URI.
50     * @param uri The URI being accessed.
51     */
52    public void enforceReadPermission(Uri uri) {
53        if (!mDelegate.isValidPreAuthorizedUri(uri)) {
54            mDelegate.getContext().enforceCallingOrSelfPermission(READ_PERMISSION, null);
55        }
56    }
57
58    /**
59     * Performs a permission check on the write profile permission.
60     */
61    public void enforceWritePermission() {
62        mDelegate.getContext().enforceCallingOrSelfPermission(WRITE_PERMISSION, null);
63    }
64
65    @Override
66    protected ProfileDatabaseHelper getDatabaseHelper(Context context) {
67        return ProfileDatabaseHelper.getInstance(context);
68    }
69
70    @Override
71    protected ThreadLocal<ContactsTransaction> getTransactionHolder() {
72        return mDelegate.getTransactionHolder();
73    }
74
75    @Override
76    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
77            String sortOrder) {
78        return query(uri, projection, selection, selectionArgs, sortOrder, null);
79    }
80
81    @Override
82    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
83            String sortOrder, CancellationSignal cancellationSignal) {
84        enforceReadPermission(uri);
85        return mDelegate.queryLocal(uri, projection, selection, selectionArgs, sortOrder, -1,
86                cancellationSignal);
87    }
88
89    @Override
90    protected Uri insertInTransaction(Uri uri, ContentValues values) {
91        enforceWritePermission();
92        useProfileDbForTransaction();
93        return mDelegate.insertInTransaction(uri, values);
94    }
95
96    @Override
97    protected int updateInTransaction(Uri uri, ContentValues values, String selection,
98            String[] selectionArgs) {
99        enforceWritePermission();
100        useProfileDbForTransaction();
101        return mDelegate.updateInTransaction(uri, values, selection, selectionArgs);
102    }
103
104    @Override
105    protected int deleteInTransaction(Uri uri, String selection, String[] selectionArgs) {
106        enforceWritePermission();
107        useProfileDbForTransaction();
108        return mDelegate.deleteInTransaction(uri, selection, selectionArgs);
109    }
110
111    @Override
112    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
113        if (mode != null && mode.contains("w")) {
114            enforceWritePermission();
115        } else {
116            enforceReadPermission(uri);
117        }
118        return mDelegate.openAssetFileLocal(uri, mode);
119    }
120
121    private void useProfileDbForTransaction() {
122        ContactsTransaction transaction = getCurrentTransaction();
123        SQLiteDatabase db = getDatabaseHelper().getWritableDatabase();
124        transaction.startTransactionForDb(db, ContactsProvider2.PROFILE_DB_TAG, this);
125    }
126
127    @Override
128    protected void notifyChange() {
129        mDelegate.notifyChange();
130    }
131
132    protected void notifyChange(boolean syncToNetwork) {
133        mDelegate.notifyChange(syncToNetwork);
134    }
135
136    protected Locale getLocale() {
137        return mDelegate.getLocale();
138    }
139
140    @Override
141    public void onBegin() {
142        mDelegate.onBeginTransactionInternal(true);
143    }
144
145    @Override
146    public void onCommit() {
147        mDelegate.onCommitTransactionInternal(true);
148        sendProfileChangedBroadcast();
149    }
150
151    @Override
152    public void onRollback() {
153        mDelegate.onRollbackTransactionInternal(true);
154    }
155
156    @Override
157    protected boolean yield(ContactsTransaction transaction) {
158        return mDelegate.yield(transaction);
159    }
160
161    @Override
162    public String getType(Uri uri) {
163        return mDelegate.getType(uri);
164    }
165
166    /** Use only for debug logging */
167    @Override
168    public String toString() {
169        return "ProfileProvider";
170    }
171
172    private void sendProfileChangedBroadcast() {
173        final Intent intent = new Intent(Intents.ACTION_PROFILE_CHANGED);
174        mDelegate.getContext().sendBroadcast(intent, READ_PERMISSION);
175    }
176}
177