• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.model;
17 
18 import android.os.Build;
19 import androidx.annotation.RequiresApi;
20 import android.telephony.PhoneNumberUtils;
21 import android.telephony.SubscriptionInfo;
22 import android.telephony.SubscriptionManager;
23 import android.telephony.TelephonyManager;
24 import android.util.Log;
25 
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.Objects;
31 
32 /**
33  * Holds data for a SIM card in the device.
34  */
35 public class SimCard {
36 
37     private static final String TAG = "SimCard";
38 
39     public static final int NO_SUBSCRIPTION_ID = -1;
40 
41     // This state is created from the info we get from the system
42     private final String mSimId;
43     private final int mSubscriptionId;
44     private final CharSequence mCarrierName;
45     private final CharSequence mDisplayName;
46     private final String mPhoneNumber;
47     private final String mCountryCode;
48 
49     // This is our own state that we associate with SIM cards. Currently these are only used
50     // in the GoogleContacts app.
51     // Note: these are logically immutable but are not final to reduce required constructor
52     // parameters
53     private boolean mDismissed = false;
54     private boolean mImported = false;
55 
56     private List<SimContact> mContacts;
57 
SimCard(SimCard other)58     public SimCard(SimCard other) {
59         mSimId = other.mSimId;
60         mSubscriptionId = other.mSubscriptionId;
61         mCarrierName = other.mCarrierName;
62         mDisplayName = other.mDisplayName;
63         mPhoneNumber = other.mPhoneNumber;
64         mCountryCode = other.mCountryCode;
65         mDismissed = other.mDismissed;
66         mImported = other.mImported;
67         if (other.mContacts != null) {
68             mContacts = new ArrayList<>(other.mContacts);
69         }
70     }
71 
SimCard(String simId, int subscriptionId, CharSequence carrierName, CharSequence displayName, String phoneNumber, String countryCode)72     public SimCard(String simId, int subscriptionId, CharSequence carrierName,
73             CharSequence displayName, String phoneNumber, String countryCode) {
74         mSimId = simId;
75         mSubscriptionId = subscriptionId;
76         mCarrierName = carrierName;
77         mDisplayName = displayName;
78         mPhoneNumber = phoneNumber;
79         mCountryCode = countryCode != null ? countryCode.toUpperCase(Locale.US) : null;
80     }
81 
getSimId()82     public String getSimId() {
83         return mSimId;
84     }
85 
getSubscriptionId()86     public int getSubscriptionId() {
87         return mSubscriptionId;
88     }
89 
hasValidSubscriptionId()90     public boolean hasValidSubscriptionId() {
91         return mSubscriptionId != NO_SUBSCRIPTION_ID;
92     }
93 
getDisplayName()94     public CharSequence getDisplayName() {
95         return mDisplayName;
96     }
97 
getPhone()98     public String getPhone() {
99         return mPhoneNumber;
100     }
101 
getFormattedPhone()102     public CharSequence getFormattedPhone() {
103         if (mPhoneNumber == null) {
104             return null;
105         }
106         return PhoneNumberUtils.formatNumber(mPhoneNumber, mCountryCode);
107     }
108 
hasPhone()109     public boolean hasPhone() {
110         return mPhoneNumber != null;
111     }
112 
getCountryCode()113     public String getCountryCode() {
114         return mCountryCode;
115     }
116 
117     /**
118      * Returns whether the contacts for this SIM card have been initialized.
119      */
areContactsAvailable()120     public boolean areContactsAvailable() {
121         return mContacts != null;
122     }
123 
124     /**
125      * Returns whether this SIM card has any SIM contacts.
126      *
127      * A precondition of this method is that the contacts have been initialized.
128      */
hasContacts()129     public boolean hasContacts() {
130         if (mContacts == null) {
131             throw new IllegalStateException("Contacts not loaded.");
132         }
133         return !mContacts.isEmpty();
134     }
135 
136     /**
137      * Returns the number of contacts stored on this SIM card.
138      *
139      * A precondition of this method is that the contacts have been initialized.
140      */
getContactCount()141     public int getContactCount() {
142         if (mContacts == null) {
143             throw new IllegalStateException("Contacts not loaded.");
144         }
145         return mContacts.size();
146     }
147 
isDismissed()148     public boolean isDismissed() {
149         return mDismissed;
150     }
151 
isImported()152     public boolean isImported() {
153         return mImported;
154     }
155 
isImportable()156     public boolean isImportable() {
157         if (Log.isLoggable(TAG, Log.DEBUG)) {
158             Log.d(TAG, "isImportable: isDismissed? " + isDismissed() +
159                     " isImported? " + isImported() + " contacts=" + mContacts);
160         }
161         return !isDismissed() && !isImported() && hasContacts();
162     }
163 
164     /**
165      * Returns the contacts for this SIM card or null if the contacts have not been initialized.
166      */
getContacts()167     public List<SimContact> getContacts() {
168         return mContacts;
169     }
170 
withImportAndDismissStates(boolean imported, boolean dismissed)171     public SimCard withImportAndDismissStates(boolean imported, boolean dismissed) {
172         SimCard copy = new SimCard(this);
173         copy.mImported = imported;
174         copy.mDismissed = dismissed;
175         return copy;
176     }
177 
withImportedState(boolean imported)178     public SimCard withImportedState(boolean imported) {
179         return withImportAndDismissStates(imported, mDismissed);
180     }
181 
withDismissedState(boolean dismissed)182     public SimCard withDismissedState(boolean dismissed) {
183         return withImportAndDismissStates(mImported, dismissed);
184     }
185 
withContacts(List<SimContact> contacts)186     public SimCard withContacts(List<SimContact> contacts) {
187         final SimCard copy = new SimCard(this);
188         copy.mContacts = contacts;
189         return copy;
190     }
191 
withContacts(SimContact... contacts)192     public SimCard withContacts(SimContact... contacts) {
193         final SimCard copy = new SimCard(this);
194         copy.mContacts = Arrays.asList(contacts);
195         return copy;
196     }
197 
198     @Override
equals(Object o)199     public boolean equals(Object o) {
200         if (this == o) return true;
201         if (o == null || getClass() != o.getClass()) return false;
202 
203         SimCard simCard = (SimCard) o;
204 
205         return mSubscriptionId == simCard.mSubscriptionId && mDismissed == simCard.mDismissed &&
206                 mImported == simCard.mImported && Objects.equals(mSimId, simCard.mSimId) &&
207                 Objects.equals(mPhoneNumber, simCard.mPhoneNumber) &&
208                 Objects.equals(mCountryCode, simCard.mCountryCode);
209     }
210 
211     @Override
hashCode()212     public int hashCode() {
213         int result = Objects.hash(mSimId, mPhoneNumber, mCountryCode);
214         result = 31 * result + mSubscriptionId;
215         result = 31 * result + (mDismissed ? 1 : 0);
216         result = 31 * result + (mImported ? 1 : 0);
217         return result;
218     }
219 
220     @Override
toString()221     public String toString() {
222         return "SimCard{" +
223                 "mSimId='" + mSimId + '\'' +
224                 ", mSubscriptionId=" + mSubscriptionId +
225                 ", mCarrierName=" + mCarrierName +
226                 ", mDisplayName=" + mDisplayName +
227                 ", mPhoneNumber='" + mPhoneNumber + '\'' +
228                 ", mCountryCode='" + mCountryCode + '\'' +
229                 ", mDismissed=" + mDismissed +
230                 ", mImported=" + mImported +
231                 ", mContacts=" + mContacts +
232                 '}';
233     }
234 
235     @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
create(SubscriptionInfo info)236     public static SimCard create(SubscriptionInfo info) {
237         return new SimCard(info.getIccId(), info.getSubscriptionId(),
238                 info.getCarrierName(), info.getDisplayName(), info.getNumber(),
239                 info.getCountryIso());
240     }
241 
create(TelephonyManager telephony, String displayLabel)242     public static SimCard create(TelephonyManager telephony, String displayLabel) {
243         if (telephony.getSimState() == TelephonyManager.SIM_STATE_READY) {
244             return new SimCard(telephony.getSimSerialNumber(), telephony.getSubscriptionId(),
245                     telephony.getSimOperatorName(), displayLabel, telephony.getLine1Number(),
246                     telephony.getSimCountryIso());
247         } else {
248             // This should never happen but in case it does just fallback to an "empty" instance
249             return new SimCard(/* SIM id */ "",
250                     /* subscriptionId */ SubscriptionManager.INVALID_SUBSCRIPTION_ID,
251                     /* operator name */ null, displayLabel,
252                     /* phone number */ "", /* Country code */ null);
253         }
254     }
255 }
256