diff options
9 files changed, 349 insertions, 30 deletions
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/phonelookup/composite/CompositePhoneLookup.java b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java index f85b357e7..59a845774 100644 --- a/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java +++ b/java/com/android/dialer/phonelookup/composite/CompositePhoneLookup.java @@ -23,7 +23,6 @@ import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DialerFutures; import com.android.dialer.phonelookup.PhoneLookup; import com.android.dialer.phonelookup.PhoneLookupInfo; -import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -60,15 +59,12 @@ public final class CompositePhoneLookup implements PhoneLookup { } return Futures.transform( Futures.allAsList(futures), - new Function<List<PhoneLookupInfo>, PhoneLookupInfo>() { - @Override - public PhoneLookupInfo apply(List<PhoneLookupInfo> infos) { - PhoneLookupInfo.Builder mergedInfo = PhoneLookupInfo.newBuilder(); - for (PhoneLookupInfo info : infos) { - mergedInfo.mergeFrom(info); - } - return mergedInfo.build(); + infos -> { + PhoneLookupInfo.Builder mergedInfo = PhoneLookupInfo.newBuilder(); + for (PhoneLookupInfo info : infos) { + mergedInfo.mergeFrom(info); } + return mergedInfo.build(); }, MoreExecutors.directExecutor()); } @@ -102,30 +98,23 @@ public final class CompositePhoneLookup implements PhoneLookup { } return Futures.transform( Futures.allAsList(futures), - new Function< - List<ImmutableMap<DialerPhoneNumber, PhoneLookupInfo>>, - ImmutableMap<DialerPhoneNumber, PhoneLookupInfo>>() { - @Override - public ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> apply( - List<ImmutableMap<DialerPhoneNumber, PhoneLookupInfo>> allMaps) { - ImmutableMap.Builder<DialerPhoneNumber, PhoneLookupInfo> combinedMap = - ImmutableMap.builder(); - for (DialerPhoneNumber dialerPhoneNumber : existingInfoMap.keySet()) { - PhoneLookupInfo.Builder combinedInfo = PhoneLookupInfo.newBuilder(); - for (ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> map : allMaps) { - PhoneLookupInfo subInfo = map.get(dialerPhoneNumber); - if (subInfo == null) { - throw new IllegalStateException( - "A sublookup didn't return an info for number: " - + LogUtil.sanitizePhoneNumber( - dialerPhoneNumber.getRawInput().getNumber())); - } - combinedInfo.mergeFrom(subInfo); + (allMaps) -> { + ImmutableMap.Builder<DialerPhoneNumber, PhoneLookupInfo> combinedMap = + ImmutableMap.builder(); + for (DialerPhoneNumber dialerPhoneNumber : existingInfoMap.keySet()) { + PhoneLookupInfo.Builder combinedInfo = PhoneLookupInfo.newBuilder(); + for (ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> map : allMaps) { + PhoneLookupInfo subInfo = map.get(dialerPhoneNumber); + if (subInfo == null) { + throw new IllegalStateException( + "A sublookup didn't return an info for number: " + + LogUtil.sanitizePhoneNumber(dialerPhoneNumber.getRawInput().getNumber())); } - combinedMap.put(dialerPhoneNumber, combinedInfo.build()); + combinedInfo.mergeFrom(subInfo); } - return combinedMap.build(); + combinedMap.put(dialerPhoneNumber, combinedInfo.build()); } + return combinedMap.build(); }, MoreExecutors.directExecutor()); } 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 @@ +<!-- + ~ 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 + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.dialer.preferredsim"> + + <application> + + <provider + android:authorities="com.android.dialer.preferredsimfallback" + android:exported="true" + android:multiprocess="false" + android:name=".impl.PreferredSimFallbackProvider" + android:readPermission="android.permission.READ_CONTACTS" + android:writePermission="android.permission.WRITE_CONTACTS"/> + + <meta-data + android:name="com.android.dialer.SUPPORTS_PREFERRED_SIM" + android:value="true"/> + <meta-data + android:name="com.android.dialer.PREFERRED_SIM_FALLBACK_AUTHORITY" + android:value="com.android.dialer.preferredsimfallback"/> + </application> +</manifest> 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"); + } + } +} diff --git a/packages.mk b/packages.mk index c8a1ee516..2cce8c6ef 100644 --- a/packages.mk +++ b/packages.mk @@ -41,6 +41,7 @@ LOCAL_AAPT_FLAGS := \ com.android.dialer.phonenumberutil \ com.android.dialer.postcall \ com.android.dialer.precall.impl \ + com.android.dialer.preferredsim.impl \ com.android.dialer.searchfragment.common \ com.android.dialer.searchfragment.cp2 \ com.android.dialer.searchfragment.list \ |