1 /* 2 * Copyright (C) 2020 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 android.provider.cts.simphonebook; 18 19 import static android.provider.SimPhonebookContract.ElementaryFiles.EF_ADN; 20 21 import static com.android.internal.telephony.testing.CursorSubject.assertThat; 22 23 import static com.google.common.truth.Truth.assertThat; 24 import static com.google.common.truth.Truth.assertWithMessage; 25 26 import android.Manifest; 27 import android.content.ContentResolver; 28 import android.content.ContentValues; 29 import android.content.Context; 30 import android.content.pm.PackageManager; 31 import android.database.Cursor; 32 import android.net.Uri; 33 import android.provider.SimPhonebookContract.ElementaryFiles; 34 import android.provider.SimPhonebookContract.SimRecords; 35 import android.telephony.SubscriptionInfo; 36 import android.telephony.SubscriptionManager; 37 import android.util.SparseBooleanArray; 38 39 import androidx.annotation.NonNull; 40 import androidx.test.core.app.ApplicationProvider; 41 import androidx.test.ext.junit.runners.AndroidJUnit4; 42 43 import com.android.compatibility.common.util.RequiredFeatureRule; 44 import com.android.compatibility.common.util.SystemUtil; 45 46 import org.junit.Assume; 47 import org.junit.Before; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.rules.RuleChain; 51 import org.junit.rules.TestRule; 52 import org.junit.runner.RunWith; 53 54 import java.util.ArrayList; 55 import java.util.List; 56 import java.util.Objects; 57 58 @RunWith(AndroidJUnit4.class) 59 public class SimPhonebookContract_ElementaryFilesTest { 60 61 // The minimum value for the ElementaryFiles.NAME_MAX_LENGTH column. 62 private static final int NAME_MAX_LENGTH_MINIMUM = 2; 63 // The minimum value for the ElementaryFiles.PHONE_NUMBER_MAX_LENGTH column. 64 private static final int PHONE_NUMBER_MAX_LENGTH_MINIMUM = 20; 65 66 private ContentResolver mResolver; 67 private SubscriptionManager mSubscriptionManager; 68 private int mValidSubscriptionId; 69 70 @Rule 71 public final TestRule mRule = RuleChain 72 .outerRule(new RequiredFeatureRule(PackageManager.FEATURE_TELEPHONY)) 73 .around(new SimPhonebookRequirementsRule()) 74 .around(new SimsCleanupRule(ElementaryFiles.EF_ADN)); 75 76 @Before setUp()77 public void setUp() { 78 Context context = ApplicationProvider.getApplicationContext(); 79 mResolver = context.getContentResolver(); 80 mSubscriptionManager = context.getSystemService(SubscriptionManager.class); 81 mValidSubscriptionId = new RemovableSims(context).getDefaultSubscriptionId(); 82 } 83 84 @Test query_includesRowForEachElementaryFileOnEachSim()85 public void query_includesRowForEachElementaryFileOnEachSim() throws Exception { 86 List<SubscriptionInfo> subscriptionInfos = SystemUtil.callWithShellPermissionIdentity( 87 () -> mSubscriptionManager.getActiveSubscriptionInfoList(), 88 Manifest.permission.READ_PHONE_STATE); 89 // FDN and SDN support are more rare and less important than ADN so we allow the test to 90 // work on a SIM where they are unsupported. This does leave a gap in coverage but given 91 // their relatively low importance as compared to ADN and the fact that the implementation 92 // should be very similar for each type it's OK to just assume good faith on the part of the 93 // implementation. 94 SparseBooleanArray fdnSupportedBySubId = new SparseBooleanArray(); 95 SparseBooleanArray sdnSupportedBySubId = new SparseBooleanArray(); 96 for (SubscriptionInfo info : subscriptionInfos) { 97 int subscriptionId = info.getSubscriptionId(); 98 try (Cursor cursor = mResolver.query( 99 ElementaryFiles.getItemUri(subscriptionId, ElementaryFiles.EF_FDN), 100 new String[]{ElementaryFiles.SUBSCRIPTION_ID}, null, null)) { 101 // If the EF is unsupported the item Uri will return an empty cursor 102 if (Objects.requireNonNull(cursor).moveToFirst()) { 103 assertWithMessage("subscriptionId") 104 .that(subscriptionId).isEqualTo(cursor.getInt(0)); 105 fdnSupportedBySubId.append(subscriptionId, true); 106 } 107 } 108 try (Cursor cursor = mResolver.query( 109 ElementaryFiles.getItemUri(subscriptionId, ElementaryFiles.EF_SDN), 110 new String[]{ElementaryFiles.SUBSCRIPTION_ID}, null, null)) { 111 // If the EF is unsupported the item Uri will return an empty cursor 112 if (Objects.requireNonNull(cursor).moveToFirst()) { 113 assertWithMessage("subscriptionId") 114 .that(subscriptionId).isEqualTo(cursor.getInt(0)); 115 sdnSupportedBySubId.append(subscriptionId, true); 116 } 117 } 118 } 119 120 try (Cursor cursor = query(ElementaryFiles.CONTENT_URI, 121 new String[]{ 122 ElementaryFiles.SLOT_INDEX, 123 ElementaryFiles.SUBSCRIPTION_ID, 124 ElementaryFiles.EF_TYPE, 125 ElementaryFiles.MAX_RECORDS, 126 ElementaryFiles.NAME_MAX_LENGTH, 127 ElementaryFiles.PHONE_NUMBER_MAX_LENGTH 128 })) { 129 130 assertThat(cursor).hasCount(subscriptionInfos.size() + fdnSupportedBySubId.size() 131 + sdnSupportedBySubId.size()); 132 cursor.moveToPosition(-1); 133 for (SubscriptionInfo info : subscriptionInfos) { 134 List<Integer> supportedEfs = new ArrayList<>(3); 135 supportedEfs.add(ElementaryFiles.EF_ADN); 136 if (fdnSupportedBySubId.get(info.getSubscriptionId())) { 137 supportedEfs.add(ElementaryFiles.EF_FDN); 138 } 139 if (sdnSupportedBySubId.get(info.getSubscriptionId())) { 140 supportedEfs.add(ElementaryFiles.EF_SDN); 141 } 142 for (int efType : supportedEfs) { 143 assertThat(cursor.moveToNext()).isTrue(); 144 assertThat(cursor) 145 .hasRowValue(ElementaryFiles.SLOT_INDEX, info.getSimSlotIndex()) 146 .hasRowValue(ElementaryFiles.SUBSCRIPTION_ID, info.getSubscriptionId()) 147 .hasRowValue(ElementaryFiles.EF_TYPE, efType); 148 assertThat(cursor).intField(ElementaryFiles.MAX_RECORDS).isAtLeast(1); 149 assertThat(cursor).intField(ElementaryFiles.NAME_MAX_LENGTH) 150 .isAtLeast(NAME_MAX_LENGTH_MINIMUM); 151 assertThat(cursor).intField(ElementaryFiles.PHONE_NUMBER_MAX_LENGTH) 152 .isAtLeast(PHONE_NUMBER_MAX_LENGTH_MINIMUM); 153 } 154 } 155 } 156 } 157 158 @Test query_nonEmptySim_countIsCorrect()159 public void query_nonEmptySim_countIsCorrect() { 160 String[] projection = { 161 ElementaryFiles.SUBSCRIPTION_ID, 162 ElementaryFiles.EF_TYPE, 163 ElementaryFiles.RECORD_COUNT 164 }; 165 ContentValues values = new ContentValues(); 166 values.put(SimRecords.PHONE_NUMBER, "5550101"); 167 Uri itemUri = mResolver.insert(SimRecords.getContentUri(mValidSubscriptionId, EF_ADN), 168 values); 169 mResolver.insert(SimRecords.getContentUri(mValidSubscriptionId, EF_ADN), values); 170 mResolver.insert(SimRecords.getContentUri(mValidSubscriptionId, EF_ADN), values); 171 172 try (Cursor adn = query( 173 ElementaryFiles.getItemUri(mValidSubscriptionId, ElementaryFiles.EF_ADN), 174 projection)) { 175 assertThat(adn).hasSingleRow(mValidSubscriptionId, ElementaryFiles.EF_ADN, 3); 176 } 177 178 mResolver.delete(itemUri, null); 179 180 try (Cursor adn = query( 181 ElementaryFiles.getItemUri(mValidSubscriptionId, ElementaryFiles.EF_ADN), 182 projection)) { 183 assertThat(adn).hasSingleRow(mValidSubscriptionId, ElementaryFiles.EF_ADN, 2); 184 } 185 } 186 187 @Test query_adnItemUri_returnsCorrectRow()188 public void query_adnItemUri_returnsCorrectRow() { 189 try (Cursor cursor = query( 190 ElementaryFiles.getItemUri(mValidSubscriptionId, ElementaryFiles.EF_ADN), 191 new String[]{ 192 ElementaryFiles.SUBSCRIPTION_ID, 193 ElementaryFiles.EF_TYPE 194 })) { 195 assertThat(cursor).hasSingleRow(mValidSubscriptionId, ElementaryFiles.EF_ADN); 196 } 197 } 198 199 @Test query_fdnItemUri_returnsCorrectRow()200 public void query_fdnItemUri_returnsCorrectRow() { 201 try (Cursor cursor = query( 202 ElementaryFiles.getItemUri(mValidSubscriptionId, ElementaryFiles.EF_FDN), 203 new String[]{ 204 ElementaryFiles.SUBSCRIPTION_ID, 205 ElementaryFiles.EF_TYPE 206 })) { 207 // Use an assumption so that the tests don't fail if the SIM doesn't support FDN. 208 Assume.assumeTrue("SIM should support EF_FDN but does not.", cursor.moveToFirst()); 209 assertThat(cursor).hasSingleRow(mValidSubscriptionId, ElementaryFiles.EF_FDN); 210 } 211 } 212 213 @Test query_sdnItemUri_returnsCorrectRow()214 public void query_sdnItemUri_returnsCorrectRow() { 215 try (Cursor cursor = query( 216 ElementaryFiles.getItemUri(mValidSubscriptionId, ElementaryFiles.EF_SDN), 217 new String[]{ 218 ElementaryFiles.SUBSCRIPTION_ID, 219 ElementaryFiles.EF_TYPE 220 })) { 221 // Use an assumption so that the tests don't fail if the SIM doesn't support SDN. 222 Assume.assumeTrue("SIM should support EF_SDN but does not.", cursor.moveToFirst()); 223 assertThat(cursor).hasSingleRow(mValidSubscriptionId, ElementaryFiles.EF_SDN); 224 } 225 } 226 227 @NonNull query(Uri uri, String[] projection)228 private Cursor query(Uri uri, String[] projection) { 229 return Objects.requireNonNull(mResolver.query(uri, projection, null, null)); 230 } 231 } 232