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

Commit 8058d919 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Refactor GNSS listeners"

parents 5304effe 6257ba86
Loading
Loading
Loading
Loading
+0 −220
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.location;

import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.RemoteException;
import android.util.ArrayMap;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;

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

/**
 * A base class to manage listeners that have a 1:N -> source:listener relationship.
 *
 * @hide
 */
abstract class AbstractListenerManager<TRequest, TListener> {

    private static class Registration<TRequest, TListener> {
        private final Executor mExecutor;
        @Nullable private TRequest mRequest;
        @Nullable private volatile TListener mListener;

        private Registration(@Nullable TRequest request, Executor executor, TListener listener) {
            Preconditions.checkArgument(listener != null, "invalid null listener/callback");
            Preconditions.checkArgument(executor != null, "invalid null executor");
            mExecutor = executor;
            mListener = listener;
            mRequest = request;
        }

        @Nullable
        public TRequest getRequest() {
            return mRequest;
        }

        private void unregister() {
            mRequest = null;
            mListener = null;
        }

        private void execute(Consumer<TListener> operation) {
            mExecutor.execute(
                    obtainRunnable(Registration<TRequest, TListener>::accept, this, operation)
                            .recycleOnUse());
        }

        private void accept(Consumer<TListener> operation) {
            TListener listener = mListener;
            if (listener == null) {
                return;
            }

            // we may be under the binder identity if a direct executor is used
            long identity = Binder.clearCallingIdentity();
            try {
                operation.accept(listener);
            } finally {
                Binder.restoreCallingIdentity(identity);
            }
        }
    }

    @GuardedBy("mListeners")
    private final ArrayMap<Object, Registration<TRequest, TListener>> mListeners =
            new ArrayMap<>();

    @GuardedBy("mListeners")
    @Nullable
    private TRequest mMergedRequest;

    public boolean addListener(@NonNull TListener listener, @NonNull Handler handler)
            throws RemoteException {
        return addInternal(/* request= */ null, listener, handler);
    }

    public boolean addListener(@NonNull TListener listener, @NonNull Executor executor)
            throws RemoteException {
        return addInternal(/* request= */ null, listener, executor);
    }

    public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
            @NonNull Handler handler) throws RemoteException {
        return addInternal(request, listener, handler);
    }

    public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
            @NonNull Executor executor) throws RemoteException {
        return addInternal(request, listener, executor);
    }

    protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
            @NonNull Handler handler) throws RemoteException {
        return addInternal(request, listener, new HandlerExecutor(handler));
    }

    protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
            @NonNull Executor executor)
            throws RemoteException {
        Preconditions.checkArgument(listener != null, "invalid null listener/callback");
        return addInternal(listener, new Registration<>(request, executor, convertKey(listener)));
    }

    private boolean addInternal(Object key, Registration<TRequest, TListener> registration)
            throws RemoteException {
        Preconditions.checkNotNull(registration);

        synchronized (mListeners) {
            boolean initialRequest = mListeners.isEmpty();

            Registration<TRequest, TListener> oldRegistration = mListeners.put(key, registration);
            if (oldRegistration != null) {
                oldRegistration.unregister();
            }
            TRequest merged = mergeRequests();

            if (initialRequest || !Objects.equals(merged, mMergedRequest)) {
                mMergedRequest = merged;
                if (!initialRequest) {
                    unregisterService();
                }
                registerService(mMergedRequest);
            }

            return true;
        }
    }

    public void removeListener(Object listener) throws RemoteException {
        synchronized (mListeners) {
            Registration<TRequest, TListener> oldRegistration = mListeners.remove(listener);
            if (oldRegistration == null) {
                return;
            }
            oldRegistration.unregister();

            boolean lastRequest = mListeners.isEmpty();
            TRequest merged = lastRequest ? null : mergeRequests();
            boolean newRequest = !lastRequest && !Objects.equals(merged, mMergedRequest);

            if (lastRequest || newRequest) {
                unregisterService();
                mMergedRequest = merged;
                if (newRequest) {
                    registerService(mMergedRequest);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    protected TListener convertKey(@NonNull Object listener) {
        return (TListener) listener;
    }

    protected abstract boolean registerService(TRequest request) throws RemoteException;
    protected abstract void unregisterService() throws RemoteException;

    @Nullable
    protected TRequest merge(@NonNull TRequest[] requests) {
        for (TRequest request : requests) {
            Preconditions.checkArgument(request == null,
                    "merge() has to be overridden for non-null requests.");
        }
        return null;
    }

    protected void execute(Consumer<TListener> operation) {
        synchronized (mListeners) {
            for (Registration<TRequest, TListener> registration : mListeners.values()) {
                registration.execute(operation);
            }
        }
    }

    @GuardedBy("mListeners")
    @SuppressWarnings("unchecked")
    @Nullable
    private TRequest mergeRequests() {
        Preconditions.checkState(Thread.holdsLock(mListeners));

        if (mListeners.isEmpty()) {
            return null;
        }

        if (mListeners.size() == 1) {
            return mListeners.valueAt(0).getRequest();
        }

        TRequest[] requests = (TRequest[]) new Object[mListeners.size()];
        for (int index = 0; index < mListeners.size(); index++) {
            requests[index] = mListeners.valueAt(index).getRequest();
        }
        return merge(requests);
    }
}
+3 −1
Original line number Diff line number Diff line
@@ -70,7 +70,9 @@ public final class GnssRequest implements Parcelable {
    public String toString() {
        StringBuilder s = new StringBuilder();
        s.append("GnssRequest[");
        s.append("FullTracking=").append(mFullTracking);
        if (mFullTracking) {
            s.append("FullTracking");
        }
        s.append(']');
        return s.toString();
    }
+13 −18
Original line number Diff line number Diff line
@@ -57,10 +57,6 @@ interface ILocationManager
            in PendingIntent intent, String packageName, String featureId);
    void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName);

    boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName,
            String featureId);
    void unregisterGnssStatusCallback(IGnssStatusListener callback);

    boolean geocoderIsPresent();
    String getFromLocation(double latitude, double longitude, int maxResults,
        in GeocoderParams params, out List<Address> addrs);
@@ -69,31 +65,30 @@ interface ILocationManager
        double upperRightLatitude, double upperRightLongitude, int maxResults,
        in GeocoderParams params, out List<Address> addrs);

    boolean addGnssMeasurementsListener(in GnssRequest request,
            in IGnssMeasurementsListener listener,
            String packageName, String featureId);
    void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
            in String packageName);
    long getGnssCapabilities();
    int getGnssYearOfHardware();
    String getGnssHardwareModelName();

    void registerGnssStatusCallback(in IGnssStatusListener callback, String packageName, String featureId);
    void unregisterGnssStatusCallback(in IGnssStatusListener callback);

    void addGnssMeasurementsListener(in GnssRequest request, in IGnssMeasurementsListener listener, String packageName, String featureId);
    void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);

    boolean addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener,
             String packageName, String featureId);
    void addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener, String packageName, String featureId);
    void removeGnssAntennaInfoListener(in IGnssAntennaInfoListener listener);

    boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener,
             String packageName, String featureId);
    void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, String featureId);
    void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);

    int getGnssYearOfHardware();
    String getGnssHardwareModelName();
    void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections, String packageName);

    int getGnssBatchSize(String packageName);
    boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName, String featureId);
    void addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName, String featureId);
    void removeGnssBatchingCallback();
    boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName, String featureId);
    void startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName, String featureId);
    void flushGnssBatch(String packageName);
    boolean stopGnssBatch();
    void stopGnssBatch();
    void injectLocation(in Location location);

    List<String> getAllProviders();
Loading