1 /*
2  * Copyright (C) 2010 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.location;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.SystemApi;
23 import android.compat.annotation.UnsupportedAppUsage;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.os.SystemClock;
27 
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.util.Locale;
31 
32 /**
33  * This class wraps the country information.
34  *
35  * @hide
36  */
37 @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
38 public final class Country implements Parcelable {
39     /**
40      * The country code came from the mobile network
41      */
42     public static final int COUNTRY_SOURCE_NETWORK = 0;
43 
44     /**
45      * The country code came from the location service
46      */
47     public static final int COUNTRY_SOURCE_LOCATION = 1;
48 
49     /**
50      * The country code was read from the SIM card
51      */
52     public static final int COUNTRY_SOURCE_SIM = 2;
53 
54     /**
55      * The country code came from the system locale setting
56      */
57     public static final int COUNTRY_SOURCE_LOCALE = 3;
58 
59     /**
60      * Country source type
61      *
62      * @hide
63      */
64     @IntDef(
65             prefix = {"COUNTRY_SOURCE_"},
66             value = {
67                     COUNTRY_SOURCE_NETWORK,
68                     COUNTRY_SOURCE_LOCATION,
69                     COUNTRY_SOURCE_SIM,
70                     COUNTRY_SOURCE_LOCALE
71             })
72     @Retention(RetentionPolicy.SOURCE)
73     public @interface CountrySource {}
74 
75     /** The ISO 3166-1 two letters country code. */
76     private final String mCountryIso;
77 
78     /**
79      * Where the country code came from.
80      */
81     private final int mSource;
82 
83     private int mHashCode;
84 
85     /**
86      * Time that this object was created (which we assume to be the time that the source was
87      * consulted). This time is in milliseconds since boot up.
88      */
89     private final long mTimestamp;
90 
91     /**
92      * @param countryIso the ISO 3166-1 two letters country code.
93      * @param source where the countryIso came from, could be one of below values
94      *     <p>
95      *     <ul>
96      *       <li>{@link #COUNTRY_SOURCE_NETWORK}
97      *       <li>{@link #COUNTRY_SOURCE_LOCATION}
98      *       <li>{@link #COUNTRY_SOURCE_SIM}
99      *       <li>{@link #COUNTRY_SOURCE_LOCALE}
100      *     </ul>
101      */
Country(@onNull final String countryIso, @CountrySource final int source)102     public Country(@NonNull final String countryIso, @CountrySource final int source) {
103         if (countryIso == null
104                 || source < COUNTRY_SOURCE_NETWORK
105                 || source > COUNTRY_SOURCE_LOCALE) {
106             throw new IllegalArgumentException();
107         }
108         mCountryIso = countryIso.toUpperCase(Locale.US);
109         mSource = source;
110         mTimestamp = SystemClock.elapsedRealtime();
111     }
112 
Country(final String countryIso, final int source, long timestamp)113     private Country(final String countryIso, final int source, long timestamp) {
114         if (countryIso == null || source < COUNTRY_SOURCE_NETWORK
115                 || source > COUNTRY_SOURCE_LOCALE) {
116             throw new IllegalArgumentException();
117         }
118         mCountryIso = countryIso.toUpperCase(Locale.US);
119         mSource = source;
120         mTimestamp = timestamp;
121     }
122 
123     /** @hide */
Country(Country country)124     public Country(Country country) {
125         mCountryIso = country.mCountryIso;
126         mSource = country.mSource;
127         mTimestamp = country.mTimestamp;
128     }
129 
130     /**
131      * @return the ISO 3166-1 two letters country code
132      *
133      * @hide
134      *
135      * @deprecated clients using getCountryIso should use the {@link #getCountryCode()} API instead.
136      */
137     @UnsupportedAppUsage
138     @Deprecated
getCountryIso()139     public String getCountryIso() {
140         return mCountryIso;
141     }
142 
143     /**
144      * Retrieves country code.
145      *
146      * @return country code in ISO 3166-1:alpha2
147      */
148     @NonNull
getCountryCode()149     public String getCountryCode() {
150         return mCountryIso;
151     }
152 
153     /**
154      * @return where the country code came from, could be one of below values
155      *         <p>
156      *         <ul>
157      *         <li>{@link #COUNTRY_SOURCE_NETWORK}</li>
158      *         <li>{@link #COUNTRY_SOURCE_LOCATION}</li>
159      *         <li>{@link #COUNTRY_SOURCE_SIM}</li>
160      *         <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
161      *         </ul>
162      */
163     @CountrySource
getSource()164     public int getSource() {
165         return mSource;
166     }
167 
168     /**
169      * Returns the time that this object was created (which we assume to be the time that the source
170      * was consulted).
171      *
172      * @hide
173      */
getTimestamp()174     public long getTimestamp() {
175         return mTimestamp;
176     }
177 
178     @android.annotation.NonNull
179     public static final Parcelable.Creator<Country> CREATOR = new Parcelable.Creator<Country>() {
180         public Country createFromParcel(Parcel in) {
181             return new Country(in.readString(), in.readInt(), in.readLong());
182         }
183 
184         public Country[] newArray(int size) {
185             return new Country[size];
186         }
187     };
188 
189     @Override
describeContents()190     public int describeContents() {
191         return 0;
192     }
193 
194     @Override
writeToParcel(@onNull Parcel parcel, int flags)195     public void writeToParcel(@NonNull Parcel parcel, int flags) {
196         parcel.writeString(mCountryIso);
197         parcel.writeInt(mSource);
198         parcel.writeLong(mTimestamp);
199     }
200 
201     /**
202      * Returns true if this {@link Country} is equivalent to the given object. This ignores
203      * the timestamp value and just checks for equivalence of countryIso and source values.
204      * Returns false otherwise.
205      *
206      */
207     @Override
equals(@ullable Object object)208     public boolean equals(@Nullable Object object) {
209         if (object == this) {
210             return true;
211         }
212         if (object instanceof Country) {
213             Country c = (Country) object;
214             // No need to check the equivalence of the timestamp
215             return mCountryIso.equals(c.getCountryIso()) && mSource == c.getSource();
216         }
217         return false;
218     }
219 
220     @Override
hashCode()221     public int hashCode() {
222         int hash = mHashCode;
223         if (hash == 0) {
224             hash = 17;
225             hash = hash * 13 + mCountryIso.hashCode();
226             hash = hash * 13 + mSource;
227             mHashCode = hash;
228         }
229         return mHashCode;
230     }
231 
232     /**
233      * Compare the specified country to this country object ignoring the source
234      * and timestamp fields, return true if the countryIso fields are equal
235      *
236      * @param country the country to compare
237      * @return true if the specified country's countryIso field is equal to this
238      *         country's, false otherwise.
239      *
240      * @hide
241      */
equalsIgnoreSource(Country country)242     public boolean equalsIgnoreSource(Country country) {
243         return country != null && mCountryIso.equals(country.getCountryIso());
244     }
245 
246     @Override
247     @NonNull
toString()248     public String toString() {
249         return "Country {ISO=" + mCountryIso + ", source=" + mSource + ", time=" + mTimestamp + "}";
250     }
251 }
252