diff options
author | Makoto Onuki <omakoto@google.com> | 2011-12-13 14:02:32 -0800 |
---|---|---|
committer | Makoto Onuki <omakoto@google.com> | 2011-12-13 14:02:32 -0800 |
commit | d73b79bb314dde86cf8ff9300fefc133b31841d1 (patch) | |
tree | a53994afb14a9a58be2a5088aa903fbe7495ff71 | |
parent | 19a06fe93cccb4b1dd224b8456969821a19b07ef (diff) | |
download | frameworks_base-d73b79bb314dde86cf8ff9300fefc133b31841d1.zip frameworks_base-d73b79bb314dde86cf8ff9300fefc133b31841d1.tar.gz frameworks_base-d73b79bb314dde86cf8ff9300fefc133b31841d1.tar.bz2 |
Cherry-picking Id45abeba and Ia065dec6 for MR1
-------------------------------------------------------
MCC detection fixes for CountryDetector
- Don't get and cache phone tpe at the initialization time. At this point
TelephonyManager is probably not ready yet.
- Refresh MCC whenever we get the service state changed callback, even when
the state hasn't actually changed, in order to make sure we get refresh
country properly when MCC changes.
- Also remove the initialization of mPhoneStateListener, which prevented us from
registering phone state listener properly.
- Also fix tests which were already failing.
Bug 5670680
-------------------------------------------------------
Add logging to country detector logic
This is for debugging purposes to verify the effects of
change Id45abeba1b1e843053ac2c946861b439ca568de4.
Bug: 5670680
Change-Id: I238d953484e2c8135f7dac70fce8662c8300a286
4 files changed, 204 insertions, 42 deletions
diff --git a/location/java/android/location/Country.java b/location/java/android/location/Country.java index 939bd4a..7c1485d 100755 --- a/location/java/android/location/Country.java +++ b/location/java/android/location/Country.java @@ -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 + "}"; + } } diff --git a/services/java/com/android/server/CountryDetectorService.java b/services/java/com/android/server/CountryDetectorService.java index 3081ebe..ab61a3c 100644 --- a/services/java/com/android/server/CountryDetectorService.java +++ b/services/java/com/android/server/CountryDetectorService.java @@ -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); + } + } } diff --git a/services/java/com/android/server/location/ComprehensiveCountryDetector.java b/services/java/com/android/server/location/ComprehensiveCountryDetector.java index bb9e60f..2d6a148 100755 --- a/services/java/com/android/server/location/ComprehensiveCountryDetector.java +++ b/services/java/com/android/server/location/ComprehensiveCountryDetector.java @@ -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,10 +58,15 @@ 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 */ private final static long LOCATION_REFRESH_INTERVAL = 1000 * 60 * 60 * 24; // 1 day @@ -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(); + } } diff --git a/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java b/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java index 60677df..5f5d668 100755 --- a/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java +++ b/services/tests/servicestests/src/com/android/server/location/LocationBasedCountryDetectorTest.java @@ -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) { |