From 66834e40d812439171b5cb670b098e41a34eea6a Mon Sep 17 00:00:00 2001 From: twyen Date: Fri, 10 Nov 2017 16:35:42 -0800 Subject: Implement PreferredSimFallbackProvider The PhoneAccountHandle is keyed with the DATA_ID of a Phone row in the Contacts provider. Bug: 64213352 Test: PreferredSimFallbackProviderTest PiperOrigin-RevId: 175354818 Change-Id: I828ff9d5a1e03e18b05256bcf8a61170be8ce7bc --- java/com/android/dialer/constants/Constants.java | 3 + .../dialer/constants/aospdialer/ConstantsImpl.java | 6 + .../constants/googledialer/ConstantsImpl.java | 6 + .../preferredsim/PreferredSimFallbackContract.java | 72 ++++++++++ .../dialer/preferredsim/impl/AndroidManifest.xml | 36 +++++ .../impl/PreferredSimDatabaseHelper.java | 56 ++++++++ .../impl/PreferredSimFallbackProvider.java | 150 +++++++++++++++++++++ 7 files changed, 329 insertions(+) create mode 100644 java/com/android/dialer/preferredsim/PreferredSimFallbackContract.java create mode 100644 java/com/android/dialer/preferredsim/impl/AndroidManifest.xml create mode 100644 java/com/android/dialer/preferredsim/impl/PreferredSimDatabaseHelper.java create mode 100644 java/com/android/dialer/preferredsim/impl/PreferredSimFallbackProvider.java (limited to 'java') diff --git a/java/com/android/dialer/constants/Constants.java b/java/com/android/dialer/constants/Constants.java index 644dd6b8f..fb1aa2ae0 100644 --- a/java/com/android/dialer/constants/Constants.java +++ b/java/com/android/dialer/constants/Constants.java @@ -60,6 +60,9 @@ public abstract class Constants { @NonNull public abstract String getPhoneLookupHistoryProviderAuthority(); + @NonNull + public abstract String getPreferredSimFallbackProviderAuthority(); + public abstract String getUserAgent(Context context); @NonNull diff --git a/java/com/android/dialer/constants/aospdialer/ConstantsImpl.java b/java/com/android/dialer/constants/aospdialer/ConstantsImpl.java index e7eab68b1..312b3f58a 100644 --- a/java/com/android/dialer/constants/aospdialer/ConstantsImpl.java +++ b/java/com/android/dialer/constants/aospdialer/ConstantsImpl.java @@ -48,6 +48,12 @@ public class ConstantsImpl extends Constants { return "com.android.dialer.phonelookuphistory"; } + @NonNull + @Override + public String getPreferredSimFallbackProviderAuthority() { + return "com.android.dialer.preferredsimfallback"; + } + @Override public String getUserAgent(Context context) { return null; diff --git a/java/com/android/dialer/constants/googledialer/ConstantsImpl.java b/java/com/android/dialer/constants/googledialer/ConstantsImpl.java index 8580fce83..e4a96feb9 100644 --- a/java/com/android/dialer/constants/googledialer/ConstantsImpl.java +++ b/java/com/android/dialer/constants/googledialer/ConstantsImpl.java @@ -50,6 +50,12 @@ public class ConstantsImpl extends Constants { return "com.google.android.dialer.phonelookuphistory"; } + @NonNull + @Override + public String getPreferredSimFallbackProviderAuthority() { + return "com.google.android.dialer.preferredsimfallback"; + } + @Override public String getUserAgent(Context context) { StringBuilder userAgent = new StringBuilder("GoogleDialer "); diff --git a/java/com/android/dialer/preferredsim/PreferredSimFallbackContract.java b/java/com/android/dialer/preferredsim/PreferredSimFallbackContract.java new file mode 100644 index 000000000..79e097c47 --- /dev/null +++ b/java/com/android/dialer/preferredsim/PreferredSimFallbackContract.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.preferredsim; + +import android.content.ComponentName; +import android.net.Uri; +import android.provider.ContactsContract.CommonDataKinds; +import android.telecom.PhoneAccountHandle; +import com.android.dialer.constants.Constants; + +/** + * Extend fields for preferred SIM that is not available in {@link + * android.provider.ContactsContract.Data} before P. Only query and update is supported for this + * provider, and the update selection must be {@link PreferredSim#UPDATE_ID_SELECTION}. Caller must + * have {@link android.Manifest.permission#READ_CONTACTS} to read or {@link + * android.Manifest.permission#WRITE_CONTACTS} to write. + */ +public final class PreferredSimFallbackContract { + + /** + * Check the meta-data "com.android.dialer.PREFERRED_SIM_FALLBACK_AUTHORITY" to get the authority + * of the default dialer if it support it. + */ + public static final String AUTHORITY = Constants.get().getPreferredSimFallbackProviderAuthority(); + + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); + + /** Columns for preferred SIM. */ + public static final class PreferredSim { + + /** + * Unique key that should match {@link + * android.provider.ContactsContract.CommonDataKinds.Phone#_ID} of the data row it is associated + * with. + */ + public static final String DATA_ID = "data_id"; + + /** + * The flattened {@link android.content.ComponentName} of a {@link PhoneAccountHandle} that is + * the preferred {@code PhoneAccountHandle} to call the contact with. Used by {@link + * CommonDataKinds.Phone}. + * + * @see PhoneAccountHandle#getComponentName() + * @see ComponentName#flattenToString() + */ + public static final String PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME = + "preferred_phone_account_component_name"; + + /** + * The ID of a {@link PhoneAccountHandle} that is the preferred {@code PhoneAccountHandle} to + * call the contact with. Used by {@link CommonDataKinds.Phone}. + * + * @see PhoneAccountHandle#getId() () + * @see ComponentName#flattenToString() + */ + public static final String PREFERRED_PHONE_ACCOUNT_ID = "preferred_phone_account_id"; + } +} diff --git a/java/com/android/dialer/preferredsim/impl/AndroidManifest.xml b/java/com/android/dialer/preferredsim/impl/AndroidManifest.xml new file mode 100644 index 000000000..e21598fc3 --- /dev/null +++ b/java/com/android/dialer/preferredsim/impl/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/java/com/android/dialer/preferredsim/impl/PreferredSimDatabaseHelper.java b/java/com/android/dialer/preferredsim/impl/PreferredSimDatabaseHelper.java new file mode 100644 index 000000000..4795ea458 --- /dev/null +++ b/java/com/android/dialer/preferredsim/impl/PreferredSimDatabaseHelper.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.preferredsim.impl; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import com.android.dialer.common.LogUtil; +import com.android.dialer.preferredsim.PreferredSimFallbackContract.PreferredSim; + +/** Database helper class for preferred SIM. */ +public class PreferredSimDatabaseHelper extends SQLiteOpenHelper { + + static final String TABLE = "preferred_sim"; + + private static final String CREATE_TABLE_SQL = + "create table if not exists " + + TABLE + + " (" + + (PreferredSim.DATA_ID + " integer primary key, ") + + (PreferredSim.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME + " text, ") + + (PreferredSim.PREFERRED_PHONE_ACCOUNT_ID + " text") + + ");"; + + PreferredSimDatabaseHelper(Context appContext) { + super(appContext, "preferred_sim.db", null, 1); + } + + @Override + public void onCreate(SQLiteDatabase db) { + LogUtil.enterBlock("PreferredSimDatabaseHelper.onCreate"); + long startTime = System.currentTimeMillis(); + db.execSQL(CREATE_TABLE_SQL); + LogUtil.i( + "PreferredSimDatabaseHelper.onCreate", + "took: %dms", + System.currentTimeMillis() - startTime); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} +} diff --git a/java/com/android/dialer/preferredsim/impl/PreferredSimFallbackProvider.java b/java/com/android/dialer/preferredsim/impl/PreferredSimFallbackProvider.java new file mode 100644 index 000000000..322baa2ef --- /dev/null +++ b/java/com/android/dialer/preferredsim/impl/PreferredSimFallbackProvider.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.preferredsim.impl; + +import android.Manifest.permission; +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import com.android.dialer.preferredsim.PreferredSimFallbackContract; +import com.android.dialer.preferredsim.PreferredSimFallbackContract.PreferredSim; + +/** + * Content provider for preferred SIM columns that is only available in ContactsProvider after P. + * Only supports {@link PreferredSimFallbackContract#CONTENT_URI} without id. Insert and delete not + * supported because there are no current use case. + * + * @see PreferredSimFallbackContract + */ +public class PreferredSimFallbackProvider extends ContentProvider { + + private static final String UPDATE_ID_SELECTION = PreferredSim.DATA_ID + " = ?"; + + private PreferredSimDatabaseHelper databaseHelper; + + @Override + public boolean onCreate() { + databaseHelper = new PreferredSimDatabaseHelper(getContext()); + return true; + } + + @Nullable + @Override + public Cursor query( + @NonNull Uri uri, + @Nullable String[] projection, + @Nullable String selection, + @Nullable String[] selectionArgs, + @Nullable String sortOrder) { + checkReadContactsPermission(); + return databaseHelper + .getReadableDatabase() + .query( + PreferredSimDatabaseHelper.TABLE, + projection, + selection, + selectionArgs, + null, + null, + sortOrder); + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + return null; + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { + throw new IllegalArgumentException("Unsupported operation"); + } + + /** + * A row should only be deleted through {@link android.provider.ContactsContract.Data}. Since + * {@link android.provider.ContactsContract.Data#_ID} is AUTOINCREMENT and could not be reused, + * rows in this database will simply be orphaned and not cleaned up. To unset preference, update + * {@link PreferredSim#PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME} and {@link + * PreferredSim#PREFERRED_PHONE_ACCOUNT_ID} to {@code null}. Delete is only allowed from dialer so + * simulator can wipe all preference. + */ + @Override + public int delete( + @NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { + if (!TextUtils.equals(getContext().getPackageName(), getCallingPackage())) { + throw new IllegalArgumentException("Unsupported operation"); + } + + return databaseHelper + .getWritableDatabase() + .delete(PreferredSimDatabaseHelper.TABLE, selection, selectionArgs); + } + + /** + * Data will be inserted if {@link PreferredSim#DATA_ID} does not already exist in the database. + * During update the whole row will be replaced. + * + * @param uri must be {@link PreferredSimFallbackContract#CONTENT_URI} + * @param values must contains exactly the keys {@link + * PreferredSim#PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME} {@link + * PreferredSim#PREFERRED_PHONE_ACCOUNT_ID}. The value may be {@code null} + * @param selection must equals "data_id = ?" + * @param selectionArgs must contains exactly the {@link PreferredSim#DATA_ID} + */ + @Override + public int update( + @NonNull Uri uri, + @Nullable ContentValues values, + @Nullable String selection, + @Nullable String[] selectionArgs) { + checkWriteContactsPermission(); + if (values == null) { + return 0; + } + if (!UPDATE_ID_SELECTION.equals(selection) + || selectionArgs == null + || selectionArgs.length != 1) { + throw new IllegalArgumentException("Unsupported operation"); + } + values.put(PreferredSim.DATA_ID, selectionArgs[0]); + if (databaseHelper.getWritableDatabase().replace(PreferredSimDatabaseHelper.TABLE, null, values) + == -1) { + throw new IllegalStateException("update failed"); + } + return 1; + } + + private void checkReadContactsPermission() { + if (getContext().checkCallingOrSelfPermission(permission.READ_CONTACTS) + == PackageManager.PERMISSION_DENIED) { + throw new SecurityException("READ_CONTACTS required"); + } + } + + private void checkWriteContactsPermission() { + if (getContext().checkCallingOrSelfPermission(permission.WRITE_CONTACTS) + == PackageManager.PERMISSION_DENIED) { + throw new SecurityException("WRITE_CONTACTS required"); + } + } +} -- cgit v1.2.3