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