1 /*
2  * Copyright (C) 2016 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 package com.android.contacts.tests;
17 
18 import static org.hamcrest.Matchers.equalTo;
19 import static org.junit.Assume.assumeThat;
20 import static org.junit.Assume.assumeTrue;
21 
22 import android.content.ContentProviderOperation;
23 import android.content.ContentProviderResult;
24 import android.content.ContentResolver;
25 import android.content.ContentValues;
26 import android.content.Context;
27 import android.content.OperationApplicationException;
28 import android.database.Cursor;
29 import android.net.Uri;
30 import android.os.RemoteException;
31 import android.provider.SimPhonebookContract;
32 import android.provider.SimPhonebookContract.ElementaryFiles;
33 import android.provider.SimPhonebookContract.SimRecords;
34 import android.telephony.TelephonyManager;
35 
36 import androidx.annotation.NonNull;
37 import androidx.test.InstrumentationRegistry;
38 
39 import com.android.contacts.database.SimContactDao;
40 import com.android.contacts.model.SimCard;
41 import com.android.contacts.model.SimContact;
42 
43 import java.util.ArrayList;
44 import java.util.List;
45 
46 public class SimContactsTestHelper {
47 
48     private final Context mContext;
49     private final TelephonyManager mTelephonyManager;
50     private final ContentResolver mResolver;
51     private final SimContactDao mSimDao;
52     private final int mSubscriptionId;
53     private final Uri mDefaultSimAdnUri;
54 
SimContactsTestHelper()55     public SimContactsTestHelper() {
56         this(InstrumentationRegistry.getTargetContext());
57     }
58 
SimContactsTestHelper(Context context)59     public SimContactsTestHelper(Context context) {
60         mContext = context;
61         mResolver = context.getContentResolver();
62         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
63         mSimDao = SimContactDao.create(context);
64         mSubscriptionId = mTelephonyManager.getSubscriptionId();
65         mDefaultSimAdnUri = SimRecords.getContentUri(
66                 mTelephonyManager.getSubscriptionId(), ElementaryFiles.EF_ADN);
67     }
68 
getSimContactCount()69     public int getSimContactCount() {
70         Cursor cursor = mContext.getContentResolver().query(mDefaultSimAdnUri,
71                 null, null, null, null);
72         try {
73             return cursor.getCount();
74         } finally {
75             cursor.close();
76         }
77     }
78 
addSimContact(String name, String number)79     public Uri addSimContact(String name, String number) {
80         ContentValues values = new ContentValues();
81         if (name != null) {
82             values.put(SimRecords.NAME, name);
83         }
84         if (number != null) {
85             values.put(SimRecords.PHONE_NUMBER, number);
86         }
87         return mResolver.insert(mDefaultSimAdnUri, values);
88     }
89 
deleteAllSimContacts()90     public ContentProviderResult[] deleteAllSimContacts()
91             throws RemoteException, OperationApplicationException {
92         final List<SimCard> sims = mSimDao.getSimCards();
93         if (sims.isEmpty()) {
94             throw new IllegalStateException("Expected SIM card");
95         }
96         final List<SimContact> contacts = mSimDao.loadContactsForSim(sims.get(0));
97         ArrayList<ContentProviderOperation> ops = new ArrayList<>();
98         for (SimContact contact : contacts) {
99             ops.add(ContentProviderOperation
100                     .newDelete(SimRecords.getItemUri(
101                             mSubscriptionId, ElementaryFiles.EF_ADN, contact.getRecordNumber()))
102                     .build());
103         }
104         return mResolver.applyBatch(SimPhonebookContract.AUTHORITY, ops);
105     }
106 
restore(ArrayList<ContentProviderOperation> restoreOps)107     public ContentProviderResult[] restore(ArrayList<ContentProviderOperation> restoreOps)
108             throws RemoteException, OperationApplicationException {
109         if (restoreOps == null) return null;
110 
111         // Remove SIM contacts because we assume that caller wants the data to be in the exact
112         // state as when the restore ops were captured.
113         deleteAllSimContacts();
114         return mResolver.applyBatch(SimPhonebookContract.AUTHORITY, restoreOps);
115     }
116 
captureRestoreSnapshot()117     public ArrayList<ContentProviderOperation> captureRestoreSnapshot() {
118         final List<SimCard> sims = mSimDao.getSimCards();
119         if (sims.isEmpty()) {
120             throw new IllegalStateException("Expected SIM card");
121         }
122         final ArrayList<SimContact> contacts = mSimDao.loadContactsForSim(sims.get(0));
123 
124         final ArrayList<ContentProviderOperation> ops = new ArrayList<>();
125         for (SimContact contact : contacts) {
126             final String[] emails = contact.getEmails();
127             if (emails != null && emails.length > 0) {
128                 throw new IllegalStateException("Cannot restore emails." +
129                         " Please manually remove SIM contacts with emails.");
130             }
131             ops.add(ContentProviderOperation
132                     .newInsert(mDefaultSimAdnUri)
133                     .withValue(SimRecords.NAME, contact.getName())
134                     .withValue(SimRecords.PHONE_NUMBER, contact.getPhone())
135                     .build());
136         }
137         return ops;
138     }
139 
deleteSimContact(@onNull Uri recordUri)140     public int deleteSimContact(@NonNull Uri recordUri) {
141         return mResolver.delete(recordUri, null);
142     }
143 
isSimReady()144     public boolean isSimReady() {
145         return mTelephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY;
146     }
147 
doesSimHaveContacts()148     public boolean doesSimHaveContacts() {
149         return isSimReady() && getSimContactCount() > 0;
150     }
151 
isSimWritable()152     public boolean isSimWritable() {
153         if (!isSimReady()) return false;
154         final String name = "writabeProbe" + System.nanoTime();
155         final Uri uri = addSimContact(name, "15095550101");
156         return uri != null && deleteSimContact(uri) == 1;
157     }
158 
assumeSimReady()159     public void assumeSimReady() {
160         assumeTrue(isSimReady());
161     }
162 
assumeHasSimContacts()163     public void assumeHasSimContacts() {
164         assumeTrue(doesSimHaveContacts());
165     }
166 
assumeSimCardAbsent()167     public void assumeSimCardAbsent() {
168         assumeThat(mTelephonyManager.getSimState(), equalTo(TelephonyManager.SIM_STATE_ABSENT));
169     }
170 
171     // The emulator reports SIM_STATE_READY but writes are ignored. This verifies that the
172     // device will actually persist writes to the SIM card.
assumeSimWritable()173     public void assumeSimWritable() {
174         assumeSimReady();
175         assumeTrue(isSimWritable());
176     }
177 }
178