am 043c5f8f: Merge "Cherry-picking Id45abeba and Ia065dec6 for MR1" into ics-mr1
* commit '043c5f8f1d9a8e9077e1c16af28509b30f00324d': Cherry-picking Id45abeba and Ia065dec6 for MR1
This commit is contained in:
@@ -18,6 +18,7 @@ package android.location;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -58,8 +59,14 @@ public class Country implements Parcelable {
|
||||
private final int mSource;
|
||||
|
||||
private int mHashCode;
|
||||
|
||||
/**
|
||||
* Time that this object was created (which we assume to be the time that the source was
|
||||
* consulted). This time is in milliseconds since boot up.
|
||||
*/
|
||||
private final long mTimestamp;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param countryIso the ISO 3166-1 two letters country code.
|
||||
* @param source where the countryIso came from, could be one of below
|
||||
* values
|
||||
@@ -78,11 +85,23 @@ public class Country implements Parcelable {
|
||||
}
|
||||
mCountryIso = countryIso.toUpperCase(Locale.US);
|
||||
mSource = source;
|
||||
mTimestamp = SystemClock.elapsedRealtime();
|
||||
}
|
||||
|
||||
private Country(final String countryIso, final int source, long timestamp) {
|
||||
if (countryIso == null || source < COUNTRY_SOURCE_NETWORK
|
||||
|| source > COUNTRY_SOURCE_LOCALE) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
mCountryIso = countryIso.toUpperCase(Locale.US);
|
||||
mSource = source;
|
||||
mTimestamp = timestamp;
|
||||
}
|
||||
|
||||
public Country(Country country) {
|
||||
mCountryIso = country.mCountryIso;
|
||||
mSource = country.mSource;
|
||||
mTimestamp = country.mTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,9 +125,17 @@ public class Country implements Parcelable {
|
||||
return mSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time that this object was created (which we assume to be the time that the source
|
||||
* was consulted).
|
||||
*/
|
||||
public final long getTimestamp() {
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Country> CREATOR = new Parcelable.Creator<Country>() {
|
||||
public Country createFromParcel(Parcel in) {
|
||||
return new Country(in.readString(), in.readInt());
|
||||
return new Country(in.readString(), in.readInt(), in.readLong());
|
||||
}
|
||||
|
||||
public Country[] newArray(int size) {
|
||||
@@ -123,8 +150,14 @@ public class Country implements Parcelable {
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeString(mCountryIso);
|
||||
parcel.writeInt(mSource);
|
||||
parcel.writeLong(mTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this {@link Country} is equivalent to the given object. This ignores
|
||||
* the timestamp value and just checks for equivalence of countryIso and source values.
|
||||
* Returns false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object == this) {
|
||||
@@ -132,6 +165,7 @@ public class Country implements Parcelable {
|
||||
}
|
||||
if (object instanceof Country) {
|
||||
Country c = (Country) object;
|
||||
// No need to check the equivalence of the timestamp
|
||||
return mCountryIso.equals(c.getCountryIso()) && mSource == c.getSource();
|
||||
}
|
||||
return false;
|
||||
@@ -150,8 +184,8 @@ public class Country implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the specified country to this country object ignoring the mSource
|
||||
* field, return true if the countryIso fields are equal
|
||||
* Compare the specified country to this country object ignoring the source
|
||||
* and timestamp fields, return true if the countryIso fields are equal
|
||||
*
|
||||
* @param country the country to compare
|
||||
* @return true if the specified country's countryIso field is equal to this
|
||||
@@ -160,4 +194,9 @@ public class Country implements Parcelable {
|
||||
public boolean equalsIgnoreSource(Country country) {
|
||||
return country != null && mCountryIso.equals(country.getCountryIso());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Country {ISO=" + mCountryIso + ", source=" + mSource + ", time=" + mTimestamp + "}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.android.server.location.ComprehensiveCountryDetector;
|
||||
@@ -30,6 +32,8 @@ import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.util.PrintWriterPrinter;
|
||||
import android.util.Printer;
|
||||
import android.util.Slog;
|
||||
|
||||
/**
|
||||
@@ -75,7 +79,7 @@ public class CountryDetectorService extends ICountryDetector.Stub implements Run
|
||||
}
|
||||
}
|
||||
|
||||
private final static String TAG = "CountryDetectorService";
|
||||
private final static String TAG = "CountryDetector";
|
||||
|
||||
private final HashMap<IBinder, Receiver> mReceivers;
|
||||
private final Context mContext;
|
||||
@@ -201,4 +205,20 @@ public class CountryDetectorService extends ICountryDetector.Stub implements Run
|
||||
boolean isSystemReady() {
|
||||
return mSystemReady;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
|
||||
try {
|
||||
final Printer p = new PrintWriterPrinter(fout);
|
||||
p.println("CountryDetectorService state:");
|
||||
p.println(" Number of listeners=" + mReceivers.keySet().size());
|
||||
if (mCountryDetector == null) {
|
||||
p.println(" ComprehensiveCountryDetector not initialized");
|
||||
} else {
|
||||
p.println(" " + mCountryDetector.toString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Failed to dump CountryDetectorService: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,16 +20,19 @@ import android.content.Context;
|
||||
import android.location.Country;
|
||||
import android.location.CountryListener;
|
||||
import android.location.Geocoder;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.PhoneStateListener;
|
||||
import android.telephony.ServiceState;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* This class is used to detect the country where the user is. The sources of
|
||||
@@ -55,9 +58,14 @@ import java.util.TimerTask;
|
||||
*/
|
||||
public class ComprehensiveCountryDetector extends CountryDetectorBase {
|
||||
|
||||
private final static String TAG = "ComprehensiveCountryDetector";
|
||||
private final static String TAG = "CountryDetector";
|
||||
/* package */ static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* Max length of logs to maintain for debugging.
|
||||
*/
|
||||
private static final int MAX_LENGTH_DEBUG_LOGS = 20;
|
||||
|
||||
/**
|
||||
* The refresh interval when the location based country was used
|
||||
*/
|
||||
@@ -66,26 +74,58 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
|
||||
protected CountryDetectorBase mLocationBasedCountryDetector;
|
||||
protected Timer mLocationRefreshTimer;
|
||||
|
||||
private final int mPhoneType;
|
||||
private Country mCountry;
|
||||
private TelephonyManager mTelephonyManager;
|
||||
private final TelephonyManager mTelephonyManager;
|
||||
private Country mCountryFromLocation;
|
||||
private boolean mStopped = false;
|
||||
private ServiceState mLastState;
|
||||
|
||||
private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
|
||||
@Override
|
||||
public void onServiceStateChanged(ServiceState serviceState) {
|
||||
// TODO: Find out how often we will be notified, if this method is called too
|
||||
// many times, let's consider querying the network.
|
||||
Slog.d(TAG, "onServiceStateChanged");
|
||||
// We only care the state change
|
||||
if (mLastState == null || mLastState.getState() != serviceState.getState()) {
|
||||
detectCountry(true, true);
|
||||
mLastState = new ServiceState(serviceState);
|
||||
}
|
||||
}
|
||||
};
|
||||
private PhoneStateListener mPhoneStateListener;
|
||||
|
||||
/**
|
||||
* List of the most recent country state changes for debugging. This should have
|
||||
* a max length of MAX_LENGTH_LOGS.
|
||||
*/
|
||||
private final ConcurrentLinkedQueue<Country> mDebugLogs = new ConcurrentLinkedQueue<Country>();
|
||||
|
||||
/**
|
||||
* Most recent {@link Country} result that was added to the debug logs {@link #mDebugLogs}.
|
||||
* We keep track of this value to help prevent adding many of the same {@link Country} objects
|
||||
* to the logs.
|
||||
*/
|
||||
private Country mLastCountryAddedToLogs;
|
||||
|
||||
/**
|
||||
* Object used to synchronize access to {@link #mLastCountryAddedToLogs}. Be careful if
|
||||
* using it to synchronize anything else in this file.
|
||||
*/
|
||||
private final Object mObject = new Object();
|
||||
|
||||
/**
|
||||
* Start time of the current session for which the detector has been active.
|
||||
*/
|
||||
private long mStartTime;
|
||||
|
||||
/**
|
||||
* Stop time of the most recent session for which the detector was active.
|
||||
*/
|
||||
private long mStopTime;
|
||||
|
||||
/**
|
||||
* The sum of all the time intervals in which the detector was active.
|
||||
*/
|
||||
private long mTotalTime;
|
||||
|
||||
/**
|
||||
* Number of {@link PhoneStateListener#onServiceStateChanged(ServiceState state)} events that
|
||||
* have occurred for the current session for which the detector has been active.
|
||||
*/
|
||||
private int mCountServiceStateChanges;
|
||||
|
||||
/**
|
||||
* Total number of {@link PhoneStateListener#onServiceStateChanged(ServiceState state)} events
|
||||
* that have occurred for all time intervals in which the detector has been active.
|
||||
*/
|
||||
private int mTotalCountServiceStateChanges;
|
||||
|
||||
/**
|
||||
* The listener for receiving the notification from LocationBasedCountryDetector.
|
||||
@@ -104,7 +144,6 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
|
||||
public ComprehensiveCountryDetector(Context context) {
|
||||
super(context);
|
||||
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
mPhoneType = mTelephonyManager.getPhoneType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,6 +154,7 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
// Note: this method in this subclass called only by tests.
|
||||
Slog.i(TAG, "Stop the detector.");
|
||||
cancelLocationRefresh();
|
||||
removePhoneStateListener();
|
||||
@@ -138,17 +178,50 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
|
||||
if (result == null) {
|
||||
result = getLocaleCountry();
|
||||
}
|
||||
addToLogs(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to add this {@link Country} to the debug logs.
|
||||
*/
|
||||
private void addToLogs(Country country) {
|
||||
if (country == null) {
|
||||
return;
|
||||
}
|
||||
// If the country (ISO and source) are the same as before, then there is no
|
||||
// need to add this country as another entry in the logs. Synchronize access to this
|
||||
// variable since multiple threads could be calling this method.
|
||||
synchronized (mObject) {
|
||||
if (mLastCountryAddedToLogs != null && mLastCountryAddedToLogs.equals(country)) {
|
||||
return;
|
||||
}
|
||||
mLastCountryAddedToLogs = country;
|
||||
}
|
||||
// Manually maintain a max limit for the list of logs
|
||||
if (mDebugLogs.size() >= MAX_LENGTH_DEBUG_LOGS) {
|
||||
mDebugLogs.poll();
|
||||
}
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Slog.d(TAG, country.toString());
|
||||
}
|
||||
mDebugLogs.add(country);
|
||||
}
|
||||
|
||||
private boolean isNetworkCountryCodeAvailable() {
|
||||
// On CDMA TelephonyManager.getNetworkCountryIso() just returns SIM country. We don't want
|
||||
// to prioritize it over location based country, so ignore it.
|
||||
final int phoneType = mTelephonyManager.getPhoneType();
|
||||
if (DEBUG) Slog.v(TAG, " phonetype=" + phoneType);
|
||||
return phoneType == TelephonyManager.PHONE_TYPE_GSM;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the country from the mobile network.
|
||||
*/
|
||||
protected Country getNetworkBasedCountry() {
|
||||
String countryIso = null;
|
||||
// TODO: The document says the result may be unreliable on CDMA networks. Shall we use
|
||||
// it on CDMA phone? We may test the Android primarily used countries.
|
||||
if (mPhoneType == TelephonyManager.PHONE_TYPE_GSM) {
|
||||
if (isNetworkCountryCodeAvailable()) {
|
||||
countryIso = mTelephonyManager.getNetworkCountryIso();
|
||||
if (!TextUtils.isEmpty(countryIso)) {
|
||||
return new Country(countryIso, Country.COUNTRY_SOURCE_NETWORK);
|
||||
@@ -226,9 +299,14 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
|
||||
removePhoneStateListener();
|
||||
stopLocationBasedDetector();
|
||||
cancelLocationRefresh();
|
||||
mStopTime = SystemClock.elapsedRealtime();
|
||||
mTotalTime += mStopTime;
|
||||
} else if (prevListener == null) {
|
||||
addPhoneStateListener();
|
||||
detectCountry(false, true);
|
||||
mStartTime = SystemClock.elapsedRealtime();
|
||||
mStopTime = 0;
|
||||
mCountServiceStateChanges = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,9 +394,9 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
|
||||
private void notifyIfCountryChanged(final Country country, final Country detectedCountry) {
|
||||
if (detectedCountry != null && mListener != null
|
||||
&& (country == null || !country.equals(detectedCountry))) {
|
||||
Slog.d(TAG,
|
||||
"The country was changed from " + country != null ? country.getCountryIso() :
|
||||
country + " to " + detectedCountry.getCountryIso());
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Slog.d(TAG, "" + country + " --> " + detectedCountry);
|
||||
}
|
||||
notifyListener(detectedCountry);
|
||||
}
|
||||
}
|
||||
@@ -356,20 +434,19 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
|
||||
}
|
||||
|
||||
protected synchronized void addPhoneStateListener() {
|
||||
if (mPhoneStateListener == null && mPhoneType == TelephonyManager.PHONE_TYPE_GSM) {
|
||||
mLastState = null;
|
||||
if (mPhoneStateListener == null) {
|
||||
mPhoneStateListener = new PhoneStateListener() {
|
||||
@Override
|
||||
public void onServiceStateChanged(ServiceState serviceState) {
|
||||
// TODO: Find out how often we will be notified, if this
|
||||
// method is called too
|
||||
// many times, let's consider querying the network.
|
||||
Slog.d(TAG, "onServiceStateChanged");
|
||||
// We only care the state change
|
||||
if (mLastState == null || mLastState.getState() != serviceState.getState()) {
|
||||
detectCountry(true, true);
|
||||
mLastState = new ServiceState(serviceState);
|
||||
mCountServiceStateChanges++;
|
||||
mTotalCountServiceStateChanges++;
|
||||
|
||||
if (!isNetworkCountryCodeAvailable()) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG) Slog.d(TAG, "onServiceStateChanged: " + serviceState.getState());
|
||||
|
||||
detectCountry(true, true);
|
||||
}
|
||||
};
|
||||
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
|
||||
@@ -386,4 +463,30 @@ public class ComprehensiveCountryDetector extends CountryDetectorBase {
|
||||
protected boolean isGeoCoderImplemented() {
|
||||
return Geocoder.isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
long currentTime = SystemClock.elapsedRealtime();
|
||||
long currentSessionLength = 0;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("ComprehensiveCountryDetector{");
|
||||
// The detector hasn't stopped yet --> still running
|
||||
if (mStopTime == 0) {
|
||||
currentSessionLength = currentTime - mStartTime;
|
||||
sb.append("timeRunning=" + currentSessionLength + ", ");
|
||||
} else {
|
||||
// Otherwise, it has already stopped, so take the last session
|
||||
sb.append("lastRunTimeLength=" + (mStopTime - mStartTime) + ", ");
|
||||
}
|
||||
sb.append("totalCountServiceStateChanges=" + mTotalCountServiceStateChanges + ", ");
|
||||
sb.append("currentCountServiceStateChanges=" + mCountServiceStateChanges + ", ");
|
||||
sb.append("totalTime=" + (mTotalTime + currentSessionLength) + ", ");
|
||||
sb.append("currentTime=" + currentTime + ", ");
|
||||
sb.append("countries=");
|
||||
for (Country country : mDebugLogs) {
|
||||
sb.append("\n " + country.toString());
|
||||
}
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
|
||||
// QueryThread should be set to NULL
|
||||
assertNull(detector.getQueryThread());
|
||||
assertTrue(countryListener.notified());
|
||||
assertEquals(countryListener.getCountry(), country);
|
||||
assertEquals("us", countryListener.getCountry().toLowerCase());
|
||||
}
|
||||
|
||||
public void testFindingCountryCancelled() {
|
||||
@@ -238,7 +238,7 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
|
||||
// QueryThread should be set to NULL
|
||||
assertNull(detector.getQueryThread());
|
||||
assertTrue(countryListener.notified());
|
||||
assertEquals(countryListener.getCountry(), country);
|
||||
assertEquals("us", countryListener.getCountry().toLowerCase());
|
||||
}
|
||||
|
||||
public void testFindingLocationCancelled() {
|
||||
@@ -339,7 +339,7 @@ public class LocationBasedCountryDetectorTest extends AndroidTestCase {
|
||||
assertNull(detector.getQueryThread());
|
||||
// CountryListener should be notified
|
||||
assertTrue(countryListener.notified());
|
||||
assertEquals(countryListener.getCountry(), country);
|
||||
assertEquals("us", countryListener.getCountry().toLowerCase());
|
||||
}
|
||||
|
||||
private void waitForTimerReset(TestCountryDetector detector) {
|
||||
|
||||
Reference in New Issue
Block a user