• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.dialer.preferredsim.impl;
18 
19 import android.Manifest.permission;
20 import android.content.ContentProvider;
21 import android.content.ContentValues;
22 import android.content.pm.PackageManager;
23 import android.database.Cursor;
24 import android.database.sqlite.SQLiteQueryBuilder;
25 import android.net.Uri;
26 import android.support.annotation.NonNull;
27 import android.support.annotation.Nullable;
28 import android.text.TextUtils;
29 import com.android.dialer.preferredsim.PreferredSimFallbackContract;
30 import com.android.dialer.preferredsim.PreferredSimFallbackContract.PreferredSim;
31 import com.google.common.collect.ImmutableMap;
32 
33 /**
34  * Content provider for preferred SIM columns that is only available in ContactsProvider after P.
35  * Only supports {@link PreferredSimFallbackContract#CONTENT_URI} without id. Insert and delete not
36  * supported because there are no current use case.
37  *
38  * @see PreferredSimFallbackContract
39  */
40 public class PreferredSimFallbackProvider extends ContentProvider {
41 
42   private static final String UPDATE_ID_SELECTION = PreferredSim.DATA_ID + " = ?";
43 
44   private static final ImmutableMap<String, String> PROJECTION_MAP =
45       ImmutableMap.of(
46           PreferredSim.DATA_ID,
47           PreferredSim.DATA_ID,
48           PreferredSim.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
49           PreferredSim.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME,
50           PreferredSim.PREFERRED_PHONE_ACCOUNT_ID,
51           PreferredSim.PREFERRED_PHONE_ACCOUNT_ID);
52 
53   private PreferredSimDatabaseHelper databaseHelper;
54 
55   @Override
onCreate()56   public boolean onCreate() {
57     databaseHelper = new PreferredSimDatabaseHelper(getContext());
58     return true;
59   }
60 
61   @Nullable
62   @Override
query( @onNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder)63   public Cursor query(
64       @NonNull Uri uri,
65       @Nullable String[] projection,
66       @Nullable String selection,
67       @Nullable String[] selectionArgs,
68       @Nullable String sortOrder) {
69     checkReadContactsPermission();
70     SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
71     queryBuilder.setStrict(true);
72     queryBuilder.setProjectionMap(PROJECTION_MAP);
73     queryBuilder.setTables(PreferredSimDatabaseHelper.TABLE);
74     return queryBuilder.query(
75         databaseHelper.getReadableDatabase(),
76         projection,
77         selection,
78         selectionArgs,
79         null,
80         null,
81         sortOrder);
82   }
83 
84   @Nullable
85   @Override
getType(@onNull Uri uri)86   public String getType(@NonNull Uri uri) {
87     return null;
88   }
89 
90   @Nullable
91   @Override
insert(@onNull Uri uri, @Nullable ContentValues values)92   public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
93     throw new IllegalArgumentException("Unsupported operation");
94   }
95 
96   /**
97    * A row should only be deleted through {@link android.provider.ContactsContract.Data}. Since
98    * {@link android.provider.ContactsContract.Data#_ID} is AUTOINCREMENT and could not be reused,
99    * rows in this database will simply be orphaned and not cleaned up. To unset preference, update
100    * {@link PreferredSim#PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME} and {@link
101    * PreferredSim#PREFERRED_PHONE_ACCOUNT_ID} to {@code null}. Delete is only allowed from dialer so
102    * simulator can wipe all preference.
103    */
104   @Override
delete( @onNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs)105   public int delete(
106       @NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
107     checkWriteContactsPermission();
108 
109     if (PreferredSimFallbackContract.CONTENT_URI.equals(uri)
110         && selection == null
111         && selectionArgs == null) {
112       return databaseHelper
113           .getWritableDatabase()
114           .delete(PreferredSimDatabaseHelper.TABLE, null, null);
115     }
116 
117     if (!TextUtils.equals(getContext().getPackageName(), getCallingPackage())) {
118       throw new IllegalArgumentException("Unsupported operation");
119     }
120 
121     return databaseHelper
122         .getWritableDatabase()
123         .delete(PreferredSimDatabaseHelper.TABLE, selection, selectionArgs);
124   }
125 
126   /**
127    * Data will be inserted if {@link PreferredSim#DATA_ID} does not already exist in the database.
128    * During update the whole row will be replaced.
129    *
130    * @param uri must be {@link PreferredSimFallbackContract#CONTENT_URI}
131    * @param values must contains exactly the keys {@link
132    *     PreferredSim#PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME} {@link
133    *     PreferredSim#PREFERRED_PHONE_ACCOUNT_ID}. The value may be {@code null}
134    * @param selection must equals "data_id = ?"
135    * @param selectionArgs must contains exactly the {@link PreferredSim#DATA_ID}
136    */
137   @Override
update( @onNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs)138   public int update(
139       @NonNull Uri uri,
140       @Nullable ContentValues values,
141       @Nullable String selection,
142       @Nullable String[] selectionArgs) {
143     checkWriteContactsPermission();
144     if (values == null) {
145       return 0;
146     }
147     if (!UPDATE_ID_SELECTION.equals(selection)
148         || selectionArgs == null
149         || selectionArgs.length != 1) {
150       throw new IllegalArgumentException("Unsupported operation");
151     }
152     values.put(PreferredSim.DATA_ID, selectionArgs[0]);
153     if (databaseHelper.getWritableDatabase().replace(PreferredSimDatabaseHelper.TABLE, null, values)
154         == -1) {
155       throw new IllegalStateException("update failed");
156     }
157     getContext().getContentResolver().notifyChange(PreferredSimFallbackContract.CONTENT_URI, null);
158     return 1;
159   }
160 
checkReadContactsPermission()161   private void checkReadContactsPermission() {
162     if (getContext().checkCallingOrSelfPermission(permission.READ_CONTACTS)
163         == PackageManager.PERMISSION_DENIED) {
164       throw new SecurityException("READ_CONTACTS required");
165     }
166   }
167 
checkWriteContactsPermission()168   private void checkWriteContactsPermission() {
169     if (getContext().checkCallingOrSelfPermission(permission.WRITE_CONTACTS)
170         == PackageManager.PERMISSION_DENIED) {
171       throw new SecurityException("WRITE_CONTACTS required");
172     }
173   }
174 }
175