1 /* 2 * Copyright (C) 2018 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.calllog.database; 18 19 import android.content.ContentValues; 20 import android.provider.CallLog.Calls; 21 import android.support.annotation.IntDef; 22 import com.android.dialer.calllog.database.contract.AnnotatedCallLogContract.AnnotatedCallLog; 23 import com.android.dialer.common.Assert; 24 import java.lang.annotation.Retention; 25 import java.lang.annotation.RetentionPolicy; 26 import java.util.function.Predicate; 27 28 /** Constraints for columns in the {@link AnnotatedCallLog}. */ 29 final class AnnotatedCallLogConstraints { 30 31 /** Type of operation the {@link ContentValues} to be checked is used for. */ 32 @Retention(RetentionPolicy.SOURCE) 33 @IntDef({Operation.INSERT, Operation.UPDATE}) 34 @interface Operation { 35 int INSERT = 1; 36 int UPDATE = 2; 37 } 38 AnnotatedCallLogConstraints()39 private AnnotatedCallLogConstraints() {} 40 41 /** 42 * Checks if the given {@link ContentValues} meets the constraints defined in this class. An 43 * {@link IllegalArgumentException} will be thrown if it doesn't. 44 */ check(ContentValues contentValues, @Operation int operationType)45 public static void check(ContentValues contentValues, @Operation int operationType) { 46 checkBooleanColumn(AnnotatedCallLog.IS_READ, contentValues, operationType); 47 checkBooleanColumn(AnnotatedCallLog.NEW, contentValues, operationType); 48 checkBooleanColumn(AnnotatedCallLog.IS_VOICEMAIL_CALL, contentValues, operationType); 49 checkCallTypeColumn(contentValues, operationType); 50 } 51 52 /** 53 * Checks a boolean column. 54 * 55 * <p>Constraints: the value must be either 0 or 1 (SQLite database has no boolean type, so the 56 * value has to be an integer). 57 */ checkBooleanColumn( String columnName, ContentValues contentValues, @Operation int operationType)58 private static void checkBooleanColumn( 59 String columnName, ContentValues contentValues, @Operation int operationType) { 60 checkColumn( 61 columnName, 62 contentValues, 63 operationType, 64 contentValuesToCheck -> { 65 Integer value = contentValuesToCheck.getAsInteger(columnName); 66 return value != null && (value == 0 || value == 1); 67 }); 68 } 69 70 /** 71 * Checks column {@link AnnotatedCallLog#CALL_TYPE}. 72 * 73 * <p>Constraints: the value must be one of {@link android.provider.CallLog.Calls#TYPE}. 74 */ checkCallTypeColumn( ContentValues contentValues, @Operation int operationType)75 private static void checkCallTypeColumn( 76 ContentValues contentValues, @Operation int operationType) { 77 checkColumn( 78 AnnotatedCallLog.CALL_TYPE, 79 contentValues, 80 operationType, 81 contentValuesToCheck -> { 82 Integer callType = contentValuesToCheck.getAsInteger(AnnotatedCallLog.CALL_TYPE); 83 return callType != null 84 && (callType == Calls.INCOMING_TYPE 85 || callType == Calls.OUTGOING_TYPE 86 || callType == Calls.MISSED_TYPE 87 || callType == Calls.VOICEMAIL_TYPE 88 || callType == Calls.REJECTED_TYPE 89 || callType == Calls.BLOCKED_TYPE 90 || callType == Calls.ANSWERED_EXTERNALLY_TYPE); 91 }); 92 } 93 checkColumn( String columnName, ContentValues contentValues, @Operation int operationType, Predicate<ContentValues> predicate)94 private static void checkColumn( 95 String columnName, 96 ContentValues contentValues, 97 @Operation int operationType, 98 Predicate<ContentValues> predicate) { 99 switch (operationType) { 100 case Operation.UPDATE: 101 if (!contentValues.containsKey(columnName)) { 102 return; 103 } 104 // fall through 105 case Operation.INSERT: 106 Assert.checkArgument( 107 predicate.test(contentValues), 108 "Column %s contains invalid value: %s", 109 columnName, 110 contentValues.get(columnName)); 111 return; 112 default: 113 throw Assert.createUnsupportedOperationFailException( 114 String.format("Unsupported operation: %s", operationType)); 115 } 116 } 117 } 118