Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 576862f5 authored by Nagendra Prasad Nagarle Basavaraju's avatar Nagendra Prasad Nagarle Basavaraju
Browse files

API Enhancement CountryDetector

-Replace CountryListener with a Consumer<Country>
-Add registerCountryDetectorCallback to take an Executor Vs addCountryListener taking Looper
-Add unregisterCountryDetectorCallback to remove Consumer<Country> callback interface
-Adding getSource to return an IntDef
-Expose Country constructor
-Adding alternative API with new naming for getCountryIso
-Refactoring addCountryListener() & removeCountryListener API's

Bug: 252989268, 258197470
CTS-Coverage-Bug: 255511190
Test: manual test && make update-api
Change-Id: I5a2f253a87d1037837bc161a80cb873a6471c229
parent e655f11c
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -5608,7 +5608,9 @@ package android.location {
  }
  public final class Country implements android.os.Parcelable {
    ctor public Country(@NonNull String, int);
    method public int describeContents();
    method @NonNull public String getCountryCode();
    method public int getSource();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field public static final int COUNTRY_SOURCE_LOCALE = 3; // 0x3
@@ -5618,6 +5620,11 @@ package android.location {
    field @NonNull public static final android.os.Parcelable.Creator<android.location.Country> CREATOR;
  }
  public class CountryDetector {
    method public void registerCountryDetectorCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Country>);
    method public void unregisterCountryDetectorCallback(@NonNull java.util.function.Consumer<android.location.Country>);
  }
  public final class GnssCapabilities implements android.os.Parcelable {
    method @Deprecated public boolean hasMeasurementCorrectionsReflectingPane();
    method @Deprecated public boolean hasNavMessages();
+75 −34
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.location;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -24,6 +25,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Locale;

/**
@@ -33,22 +36,48 @@ import java.util.Locale;
 */
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
public final class Country implements Parcelable {
    /** The country code came from the mobile network */
    /**
     * The country code came from the mobile network
     */
    public static final int COUNTRY_SOURCE_NETWORK = 0;

    /** The country code came from the location service */
    /**
     * The country code came from the location service
     */
    public static final int COUNTRY_SOURCE_LOCATION = 1;

    /** The country code was read from the SIM card */
    /**
     * The country code was read from the SIM card
     */
    public static final int COUNTRY_SOURCE_SIM = 2;

    /** The country code came from the system locale setting */
    /**
     * The country code came from the system locale setting
     */
    public static final int COUNTRY_SOURCE_LOCALE = 3;

    /**
     * Country source type
     *
     * @hide
     */
    @IntDef(
            prefix = {"COUNTRY_SOURCE_"},
            value = {
                    COUNTRY_SOURCE_NETWORK,
                    COUNTRY_SOURCE_LOCATION,
                    COUNTRY_SOURCE_SIM,
                    COUNTRY_SOURCE_LOCALE
            })
    @Retention(RetentionPolicy.SOURCE)
    public @interface CountrySource {}

    /** The ISO 3166-1 two letters country code. */
    private final String mCountryIso;

    /** Where the country code came from. */
    /**
     * Where the country code came from.
     */
    private final int mSource;

    private int mHashCode;
@@ -69,11 +98,8 @@ public final class Country implements Parcelable {
     *       <li>{@link #COUNTRY_SOURCE_SIM}
     *       <li>{@link #COUNTRY_SOURCE_LOCALE}
     *     </ul>
     *
     * @hide
     */
    @UnsupportedAppUsage
    public Country(@NonNull final String countryIso, final int source) {
    public Country(@NonNull final String countryIso, @CountrySource final int source) {
        if (countryIso == null
                || source < COUNTRY_SOURCE_NETWORK
                || source > COUNTRY_SOURCE_LOCALE) {
@@ -85,8 +111,7 @@ public final class Country implements Parcelable {
    }

    private Country(final String countryIso, final int source, long timestamp) {
        if (countryIso == null
                || source < COUNTRY_SOURCE_NETWORK
        if (countryIso == null || source < COUNTRY_SOURCE_NETWORK
                || source > COUNTRY_SOURCE_LOCALE) {
            throw new IllegalArgumentException();
        }
@@ -104,23 +129,38 @@ public final class Country implements Parcelable {

    /**
     * @return the ISO 3166-1 two letters country code
     *
     * @hide
     *
     * @deprecated clients using getCountryIso should use the {@link #getCountryCode()} API instead.
     */
    @UnsupportedAppUsage
    @Deprecated
    public String getCountryIso() {
        return mCountryIso;
    }

    /**
     * Retrieves country code.
     *
     * @return country code in ISO 3166-1:alpha2
     */
    @NonNull
    public String getCountryCode() {
        return mCountryIso;
    }

    /**
     * @return where the country code came from, could be one of below values
     *         <p>
     *         <ul>
     *       <li>{@link #COUNTRY_SOURCE_NETWORK}
     *       <li>{@link #COUNTRY_SOURCE_LOCATION}
     *       <li>{@link #COUNTRY_SOURCE_SIM}
     *       <li>{@link #COUNTRY_SOURCE_LOCALE}
     *         <li>{@link #COUNTRY_SOURCE_NETWORK}</li>
     *         <li>{@link #COUNTRY_SOURCE_LOCATION}</li>
     *         <li>{@link #COUNTRY_SOURCE_SIM}</li>
     *         <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
     *         </ul>
     */
    @CountrySource
    public int getSource() {
        return mSource;
    }
@@ -136,8 +176,7 @@ public final class Country implements Parcelable {
    }

    @android.annotation.NonNull
    public static final Parcelable.Creator<Country> CREATOR =
            new Parcelable.Creator<Country>() {
    public static final Parcelable.Creator<Country> CREATOR = new Parcelable.Creator<Country>() {
        public Country createFromParcel(Parcel in) {
            return new Country(in.readString(), in.readInt(), in.readLong());
        }
@@ -160,9 +199,10 @@ public final class Country implements Parcelable {
    }

    /**
     * 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.
     * 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(@Nullable Object object) {
@@ -190,12 +230,13 @@ public final class Country implements Parcelable {
    }

    /**
     * Compare the specified country to this country object ignoring the source and timestamp
     * fields, 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 country's, false
     *     otherwise.
     * @return true if the specified country's countryIso field is equal to this
     *         country's, false otherwise.
     *
     * @hide
     */
    public boolean equalsIgnoreSource(Country country) {
+65 −36
Original line number Diff line number Diff line
@@ -16,16 +16,22 @@

package android.location;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;

import java.util.HashMap;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * This class provides access to the system country detector service. This service allows
@@ -48,42 +54,38 @@ import java.util.HashMap;
 *
 * @hide
 */
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
@SystemService(Context.COUNTRY_DETECTOR)
public class CountryDetector {

    /**
     * The class to wrap the ICountryListener.Stub and CountryListener objects together. The
     * CountryListener will be notified through the specific looper once the country changed and
     * detected.
     * The class to wrap the ICountryListener.Stub , CountryListener & {@code Consumer<Country>}
     * objects together.
     *
     * <p>The CountryListener will be notified through the Handler Executor once the country changed
     * and detected.
     *
     * <p>{@code Consumer<Country>} callback interface is notified through the specific executor
     * once the country changed and detected.
     */
    private static final class ListenerTransport extends ICountryListener.Stub {

        private final CountryListener mListener;

        private final Handler mHandler;
        private final Consumer<Country> mListener;
        private final Executor mExecutor;

        public ListenerTransport(CountryListener listener, Looper looper) {
            mListener = listener;
            if (looper != null) {
                mHandler = new Handler(looper);
            } else {
                mHandler = new Handler();
            }
        ListenerTransport(Consumer<Country> consumer, Executor executor) {
            mListener = consumer;
            mExecutor = executor;
        }

        public void onCountryDetected(final Country country) {
            mHandler.post(
                    new Runnable() {
                        public void run() {
                            mListener.onCountryDetected(country);
                        }
                    });
            mExecutor.execute(() -> mListener.accept(country));
        }
    }

    private static final String TAG = "CountryDetector";
    private final ICountryDetector mService;
    private final HashMap<CountryListener, ListenerTransport> mListeners;
    private final HashMap<Consumer<Country>, ListenerTransport> mListeners;

    /**
     * @hide - hide this constructor because it has a parameter of type ICountryDetector, which is a
@@ -93,13 +95,14 @@ public class CountryDetector {
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public CountryDetector(ICountryDetector service) {
        mService = service;
        mListeners = new HashMap<CountryListener, ListenerTransport>();
        mListeners = new HashMap<>();
    }

    /**
     * Start detecting the country that the user is in.
     *
     * @return the country if it is available immediately, otherwise null will be returned.
     * @hide
     */
    @UnsupportedAppUsage
    public Country detectCountry() {
@@ -117,33 +120,59 @@ public class CountryDetector {
     * @param listener will be called when the country is detected or changed.
     * @param looper a Looper object whose message queue will be used to implement the callback
     *     mechanism. If looper is null then the callbacks will be called on the main thread.
     * @hide
     * @deprecated client using this api should use {@link
     *     #registerCountryDetectorCallback(Executor, Consumer)} }
     */
    @UnsupportedAppUsage
    public void addCountryListener(CountryListener listener, Looper looper) {
    @Deprecated
    public void addCountryListener(@NonNull CountryListener listener, @Nullable Looper looper) {
        Handler handler = looper != null ? new Handler(looper) : new Handler();
        registerCountryDetectorCallback(new HandlerExecutor(handler), listener);
    }

    /**
     * Remove the listener
     *
     * @hide
     * @deprecated client using this api should use {@link
     *     #unregisterCountryDetectorCallback(Consumer)}
     */
    @UnsupportedAppUsage
    @Deprecated
    public void removeCountryListener(CountryListener listener) {
        unregisterCountryDetectorCallback(listener);
    }

    /**
     * Add a callback interface, to be notified when country code is added or changes.
     *
     * @param executor The callback executor for the response.
     * @param consumer {@link Consumer} callback to receive the country code when changed/detected
     */
    public void registerCountryDetectorCallback(
            @NonNull Executor executor, @NonNull Consumer<Country> consumer) {
        synchronized (mListeners) {
            if (!mListeners.containsKey(listener)) {
                ListenerTransport transport = new ListenerTransport(listener, looper);
            unregisterCountryDetectorCallback(consumer);
            ListenerTransport transport = new ListenerTransport(consumer, executor);
            try {
                mService.addCountryListener(transport);
                    mListeners.put(listener, transport);
                mListeners.put(consumer, transport);
            } catch (RemoteException e) {
                    Log.e(TAG, "addCountryListener: RemoteException", e);
                }
                throw e.rethrowFromSystemServer();
            }
        }
    }

    /** Remove the listener */
    @UnsupportedAppUsage
    public void removeCountryListener(CountryListener listener) {
    /** Remove the callback subscribed to Update country code */
    public void unregisterCountryDetectorCallback(@NonNull Consumer<Country> consumer) {
        synchronized (mListeners) {
            ListenerTransport transport = mListeners.get(listener);
            ListenerTransport transport = mListeners.remove(consumer);
            if (transport != null) {
                try {
                    mListeners.remove(listener);
                    mService.removeCountryListener(transport);
                } catch (RemoteException e) {
                    Log.e(TAG, "removeCountryListener: RemoteException", e);
                    throw e.rethrowFromSystemServer();
                }
            }
        }
+10 −1
Original line number Diff line number Diff line
@@ -18,16 +18,25 @@ package android.location;

import android.compat.annotation.UnsupportedAppUsage;

import java.util.function.Consumer;

/**
 * The listener for receiving the notification when the country is detected or
 * changed
 *
 * @hide
 */
public interface CountryListener {
public interface CountryListener extends Consumer<Country> {
    /**
     * @param country the changed or detected country.
     */
    @UnsupportedAppUsage
    void onCountryDetected(Country country);

    /**
     * @param country the changed or detected country.
     */
    default void accept(Country country) {
        onCountryDetected(country);
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -148,6 +148,10 @@ public class CountryDetectorService extends ICountryDetector.Stub {
            Receiver r = new Receiver(listener);
            try {
                listener.asBinder().linkToDeath(r, 0);
                final Country country = detectCountry();
                if (country != null) {
                    listener.onCountryDetected(country);
                }
                mReceivers.put(listener.asBinder(), r);
                if (mReceivers.size() == 1) {
                    Slog.d(TAG, "The first listener is added");