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

Commit b219baa0 authored by Soonil Nagarkar's avatar Soonil Nagarkar
Browse files

Refactor listener multiplexer

-Simplifies class structure around ListenerRegistration, moving
responsibility for requests into subclasses, adding an onRemove()
callback, and simplifying the overall class structure.
-Eliminates two locks (1 in ListenerMultiplexer, 1 in
LocationProviderManager) in favor of sharing the same lock. This
simplifies locking and reduces the changes of deadlock by messing
something up.
-Fixes a bug around callback invocation ordering
ListenerMultiplexer.onRegistrationReplaced.
-Overall normalizes ListenerMultiplexer usages with respect to other
codebases.

Test: presubmits
Change-Id: I8ad92c1ffe802eee17f5a5774c8ecee1d875252f
parent 3f58fb59
Loading
Loading
Loading
Loading
+27 −2
Original line number Original line Diff line number Diff line
@@ -124,15 +124,22 @@ public final class CallerIdentity {
                packageName, attributionTag, listenerId);
                packageName, attributionTag, listenerId);
    }
    }


    // in some tests these constants are loaded too early leading to an "incorrect" view of the
    // current pid and uid. load lazily to prevent this problem in tests.
    private static class Loader {
        private static final int MY_UID = Process.myUid();
        private static final int MY_PID = Process.myPid();
    }

    private final int mUid;
    private final int mUid;


    private final int mPid;
    private final int mPid;


    private final String mPackageName;
    private final String mPackageName;


    private final @Nullable String mAttributionTag;
    @Nullable private final String mAttributionTag;


    private final @Nullable String mListenerId;
    @Nullable private final String mListenerId;


    private CallerIdentity(int uid, int pid, String packageName,
    private CallerIdentity(int uid, int pid, String packageName,
            @Nullable String attributionTag, @Nullable String listenerId) {
            @Nullable String attributionTag, @Nullable String listenerId) {
@@ -181,6 +188,24 @@ public final class CallerIdentity {
        return mUid == Process.SYSTEM_UID;
        return mUid == Process.SYSTEM_UID;
    }
    }


    /** Returns true if this identity represents the same user this code is running in. */
    public boolean isMyUser() {
        return UserHandle.getUserId(mUid) == UserHandle.getUserId(Loader.MY_UID);
    }

    /** Returns true if this identity represents the same uid this code is running in. */
    public boolean isMyUid() {
        return mUid == Loader.MY_UID;
    }

    /**
     * Returns true if this identity represents the same process this code is running in. Returns
     * false if the identity process is unknown.
     */
    public boolean isMyProcess() {
        return mPid == Loader.MY_PID;
    }

    /**
    /**
     * Adds this identity to the worksource supplied, or if not worksource is supplied, creates a
     * Adds this identity to the worksource supplied, or if not worksource is supplied, creates a
     * new worksource representing this identity.
     * new worksource representing this identity.
+3 −0
Original line number Original line Diff line number Diff line
@@ -26,8 +26,10 @@ import android.app.AppOpsManager;
import android.content.Context;
import android.content.Context;
import android.os.Binder;
import android.os.Binder;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/** Utility class for dealing with location permissions. */
/** Utility class for dealing with location permissions. */
public final class LocationPermissions {
public final class LocationPermissions {
@@ -49,6 +51,7 @@ public final class LocationPermissions {
     */
     */
    public static final int PERMISSION_FINE = 2;
    public static final int PERMISSION_FINE = 2;


    @Target(ElementType.TYPE_USE)
    @IntDef({PERMISSION_NONE, PERMISSION_COARSE, PERMISSION_FINE})
    @IntDef({PERMISSION_NONE, PERMISSION_COARSE, PERMISSION_FINE})
    @Retention(RetentionPolicy.SOURCE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface PermissionLevel {}
    public @interface PermissionLevel {}
+0 −60
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.server.location.geofence;

import android.app.PendingIntent;
import android.location.Geofence;

import com.android.server.location.listeners.PendingIntentListenerRegistration;

import java.util.Objects;

// geofencing unfortunately allows multiple geofences under the same pending intent, even though
// this makes no real sense. therefore we manufacture an artificial key to use (pendingintent +
// geofence) instead of (pendingintent).
final class GeofenceKey  implements PendingIntentListenerRegistration.PendingIntentKey {

    private final PendingIntent mPendingIntent;
    private final Geofence mGeofence;

    GeofenceKey(PendingIntent pendingIntent, Geofence geofence) {
        mPendingIntent = Objects.requireNonNull(pendingIntent);
        mGeofence = Objects.requireNonNull(geofence);
    }

    @Override
    public PendingIntent getPendingIntent() {
        return mPendingIntent;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof GeofenceKey)) {
            return false;
        }
        GeofenceKey that = (GeofenceKey) o;
        return mPendingIntent.equals(that.mPendingIntent) && mGeofence.equals(that.mGeofence);
    }

    @Override
    public int hashCode() {
        return mPendingIntent.hashCode();
    }
}
+81 −28
Original line number Original line Diff line number Diff line
@@ -59,8 +59,8 @@ import java.util.Objects;
 * Manages all geofences.
 * Manages all geofences.
 */
 */
public class GeofenceManager extends
public class GeofenceManager extends
        ListenerMultiplexer<GeofenceKey, PendingIntent, GeofenceManager.GeofenceRegistration,
        ListenerMultiplexer<GeofenceManager.GeofenceKey, PendingIntent,
                LocationRequest> implements
                GeofenceManager.GeofenceRegistration, LocationRequest> implements
        LocationListener {
        LocationListener {


    private static final String TAG = "GeofenceManager";
    private static final String TAG = "GeofenceManager";
@@ -73,13 +73,49 @@ public class GeofenceManager extends
    private static final long MAX_LOCATION_AGE_MS = 5 * 60 * 1000L; // five minutes
    private static final long MAX_LOCATION_AGE_MS = 5 * 60 * 1000L; // five minutes
    private static final long MAX_LOCATION_INTERVAL_MS = 2 * 60 * 60 * 1000; // two hours
    private static final long MAX_LOCATION_INTERVAL_MS = 2 * 60 * 60 * 1000; // two hours


    protected final class GeofenceRegistration extends
    // geofencing unfortunately allows multiple geofences under the same pending intent, even though
            PendingIntentListenerRegistration<Geofence, PendingIntent> {
    // this makes no real sense. therefore we manufacture an artificial key to use (pendingintent +
    // geofence) instead of (pendingintent).
    static class GeofenceKey {

        private final PendingIntent mPendingIntent;
        private final Geofence mGeofence;

        GeofenceKey(PendingIntent pendingIntent, Geofence geofence) {
            mPendingIntent = Objects.requireNonNull(pendingIntent);
            mGeofence = Objects.requireNonNull(geofence);
        }

        public PendingIntent getPendingIntent() {
            return mPendingIntent;
        }

        @Override
        public boolean equals(Object o) {
            if (o instanceof GeofenceKey) {
                GeofenceKey that = (GeofenceKey) o;
                return mPendingIntent.equals(that.mPendingIntent) && mGeofence.equals(
                        that.mGeofence);
            }

            return false;
        }

        @Override
        public int hashCode() {
            return mPendingIntent.hashCode();
        }
    }

    protected class GeofenceRegistration extends
            PendingIntentListenerRegistration<GeofenceKey, PendingIntent> {


        private static final int STATE_UNKNOWN = 0;
        private static final int STATE_UNKNOWN = 0;
        private static final int STATE_INSIDE = 1;
        private static final int STATE_INSIDE = 1;
        private static final int STATE_OUTSIDE = 2;
        private static final int STATE_OUTSIDE = 2;


        private final Geofence mGeofence;
        private final CallerIdentity mIdentity;
        private final Location mCenter;
        private final Location mCenter;
        private final PowerManager.WakeLock mWakeLock;
        private final PowerManager.WakeLock mWakeLock;


@@ -89,13 +125,15 @@ public class GeofenceManager extends
        // spam us, and because checking the values may be more expensive
        // spam us, and because checking the values may be more expensive
        private boolean mPermitted;
        private boolean mPermitted;


        private @Nullable Location mCachedLocation;
        @Nullable private Location mCachedLocation;
        private float mCachedLocationDistanceM;
        private float mCachedLocationDistanceM;


        protected GeofenceRegistration(Geofence geofence, CallerIdentity identity,
        GeofenceRegistration(Geofence geofence, CallerIdentity identity,
                PendingIntent pendingIntent) {
                PendingIntent pendingIntent) {
            super(geofence, identity, pendingIntent);
            super(pendingIntent);


            mGeofence = geofence;
            mIdentity = identity;
            mCenter = new Location("");
            mCenter = new Location("");
            mCenter.setLatitude(geofence.getLatitude());
            mCenter.setLatitude(geofence.getLatitude());
            mCenter.setLongitude(geofence.getLongitude());
            mCenter.setLongitude(geofence.getLongitude());
@@ -107,16 +145,36 @@ public class GeofenceManager extends
            mWakeLock.setWorkSource(identity.addToWorkSource(null));
            mWakeLock.setWorkSource(identity.addToWorkSource(null));
        }
        }


        public Geofence getGeofence() {
            return mGeofence;
        }

        public CallerIdentity getIdentity() {
            return mIdentity;
        }

        @Override
        public String getTag() {
            return TAG;
        }

        @Override
        protected PendingIntent getPendingIntentFromKey(GeofenceKey geofenceKey) {
            return geofenceKey.getPendingIntent();
        }

        @Override
        @Override
        protected GeofenceManager getOwner() {
        protected GeofenceManager getOwner() {
            return GeofenceManager.this;
            return GeofenceManager.this;
        }
        }


        @Override
        @Override
        protected void onPendingIntentListenerRegister() {
        protected void onRegister() {
            super.onRegister();

            mGeofenceState = STATE_UNKNOWN;
            mGeofenceState = STATE_UNKNOWN;
            mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
            mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
                    getIdentity());
                    mIdentity);
        }
        }


        @Override
        @Override
@@ -132,7 +190,7 @@ public class GeofenceManager extends
        }
        }


        boolean onLocationPermissionsChanged(@Nullable String packageName) {
        boolean onLocationPermissionsChanged(@Nullable String packageName) {
            if (packageName == null || getIdentity().getPackageName().equals(packageName)) {
            if (packageName == null || mIdentity.getPackageName().equals(packageName)) {
                return onLocationPermissionsChanged();
                return onLocationPermissionsChanged();
            }
            }


@@ -140,7 +198,7 @@ public class GeofenceManager extends
        }
        }


        boolean onLocationPermissionsChanged(int uid) {
        boolean onLocationPermissionsChanged(int uid) {
            if (getIdentity().getUid() == uid) {
            if (mIdentity.getUid() == uid) {
                return onLocationPermissionsChanged();
                return onLocationPermissionsChanged();
            }
            }


@@ -149,7 +207,7 @@ public class GeofenceManager extends


        private boolean onLocationPermissionsChanged() {
        private boolean onLocationPermissionsChanged() {
            boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
            boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
                    getIdentity());
                    mIdentity);
            if (permitted != mPermitted) {
            if (permitted != mPermitted) {
                mPermitted = permitted;
                mPermitted = permitted;
                return true;
                return true;
@@ -164,12 +222,12 @@ public class GeofenceManager extends
                mCachedLocationDistanceM = mCenter.distanceTo(mCachedLocation);
                mCachedLocationDistanceM = mCenter.distanceTo(mCachedLocation);
            }
            }


            return Math.abs(getRequest().getRadius() - mCachedLocationDistanceM);
            return Math.abs(mGeofence.getRadius() - mCachedLocationDistanceM);
        }
        }


        ListenerOperation<PendingIntent> onLocationChanged(Location location) {
        ListenerOperation<PendingIntent> onLocationChanged(Location location) {
            // remove expired fences
            // remove expired fences
            if (getRequest().isExpired()) {
            if (mGeofence.isExpired()) {
                remove();
                remove();
                return null;
                return null;
            }
            }
@@ -178,7 +236,7 @@ public class GeofenceManager extends
            mCachedLocationDistanceM = mCenter.distanceTo(mCachedLocation);
            mCachedLocationDistanceM = mCenter.distanceTo(mCachedLocation);


            int oldState = mGeofenceState;
            int oldState = mGeofenceState;
            float radius = Math.max(getRequest().getRadius(), location.getAccuracy());
            float radius = Math.max(mGeofence.getRadius(), location.getAccuracy());
            if (mCachedLocationDistanceM <= radius) {
            if (mCachedLocationDistanceM <= radius) {
                mGeofenceState = STATE_INSIDE;
                mGeofenceState = STATE_INSIDE;
                if (oldState != STATE_INSIDE) {
                if (oldState != STATE_INSIDE) {
@@ -206,14 +264,14 @@ public class GeofenceManager extends
                        null, null, PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
                        null, null, PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
            } catch (PendingIntent.CanceledException e) {
            } catch (PendingIntent.CanceledException e) {
                mWakeLock.release();
                mWakeLock.release();
                removeRegistration(new GeofenceKey(pendingIntent, getRequest()), this);
                removeRegistration(new GeofenceKey(pendingIntent, mGeofence), this);
            }
            }
        }
        }


        @Override
        @Override
        public String toString() {
        public String toString() {
            StringBuilder builder = new StringBuilder();
            StringBuilder builder = new StringBuilder();
            builder.append(getIdentity());
            builder.append(mIdentity);


            ArraySet<String> flags = new ArraySet<>(1);
            ArraySet<String> flags = new ArraySet<>(1);
            if (!mPermitted) {
            if (!mPermitted) {
@@ -223,7 +281,7 @@ public class GeofenceManager extends
                builder.append(" ").append(flags);
                builder.append(" ").append(flags);
            }
            }


            builder.append(" ").append(getRequest());
            builder.append(" ").append(mGeofence);
            return builder.toString();
            return builder.toString();
        }
        }
    }
    }
@@ -258,10 +316,10 @@ public class GeofenceManager extends
    protected final LocationUsageLogger mLocationUsageLogger;
    protected final LocationUsageLogger mLocationUsageLogger;


    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private @Nullable LocationManager mLocationManager;
    @Nullable private LocationManager mLocationManager;


    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private @Nullable Location mLastLocation;
    @Nullable private Location mLastLocation;


    public GeofenceManager(Context context, Injector injector) {
    public GeofenceManager(Context context, Injector injector) {
        mContext = context.createAttributionContext(ATTRIBUTION_TAG);
        mContext = context.createAttributionContext(ATTRIBUTION_TAG);
@@ -271,11 +329,6 @@ public class GeofenceManager extends
        mLocationUsageLogger = injector.getLocationUsageLogger();
        mLocationUsageLogger = injector.getLocationUsageLogger();
    }
    }


    @Override
    public String getTag() {
        return TAG;
    }

    private LocationManager getLocationManager() {
    private LocationManager getLocationManager() {
        synchronized (mLock) {
        synchronized (mLock) {
            if (mLocationManager == null) {
            if (mLocationManager == null) {
@@ -375,7 +428,7 @@ public class GeofenceManager extends
                /* LocationRequest= */ null,
                /* LocationRequest= */ null,
                /* hasListener= */ false,
                /* hasListener= */ false,
                true,
                true,
                registration.getRequest(), true);
                registration.getGeofence(), true);
    }
    }


    @Override
    @Override
@@ -389,7 +442,7 @@ public class GeofenceManager extends
                /* LocationRequest= */ null,
                /* LocationRequest= */ null,
                /* hasListener= */ false,
                /* hasListener= */ false,
                true,
                true,
                registration.getRequest(), true);
                registration.getGeofence(), true);
    }
    }


    @Override
    @Override
@@ -417,7 +470,7 @@ public class GeofenceManager extends
        WorkSource workSource = null;
        WorkSource workSource = null;
        double minFenceDistanceM = Double.MAX_VALUE;
        double minFenceDistanceM = Double.MAX_VALUE;
        for (GeofenceRegistration registration : registrations) {
        for (GeofenceRegistration registration : registrations) {
            if (registration.getRequest().isExpired(realtimeMs)) {
            if (registration.getGeofence().isExpired(realtimeMs)) {
                continue;
                continue;
            }
            }


+23 −8
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.server.location.gnss;
package com.android.server.location.gnss;


import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.location.gnss.GnssManagerService.TAG;
import static com.android.server.location.gnss.GnssManagerService.TAG;


import android.annotation.Nullable;
import android.annotation.Nullable;
@@ -25,6 +26,7 @@ import android.location.util.identity.CallerIdentity;
import android.os.Binder;
import android.os.Binder;
import android.os.IBinder;
import android.os.IBinder;


import com.android.server.FgThread;
import com.android.server.location.gnss.hal.GnssNative;
import com.android.server.location.gnss.hal.GnssNative;
import com.android.server.location.listeners.BinderListenerRegistration;
import com.android.server.location.listeners.BinderListenerRegistration;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.listeners.ListenerMultiplexer;
@@ -45,17 +47,35 @@ public class GnssAntennaInfoProvider extends
     * Registration object for GNSS listeners.
     * Registration object for GNSS listeners.
     */
     */
    protected class AntennaInfoListenerRegistration extends
    protected class AntennaInfoListenerRegistration extends
            BinderListenerRegistration<Void, IGnssAntennaInfoListener> {
            BinderListenerRegistration<IBinder, IGnssAntennaInfoListener> {


        protected AntennaInfoListenerRegistration(CallerIdentity callerIdentity,
        private final CallerIdentity mIdentity;

        protected AntennaInfoListenerRegistration(CallerIdentity identity,
                IGnssAntennaInfoListener listener) {
                IGnssAntennaInfoListener listener) {
            super(null, callerIdentity, listener);
            super(identity.isMyProcess() ? FgThread.getExecutor() : DIRECT_EXECUTOR, listener);
            mIdentity = identity;
        }

        @Override
        protected String getTag() {
            return TAG;
        }
        }


        @Override
        @Override
        protected GnssAntennaInfoProvider getOwner() {
        protected GnssAntennaInfoProvider getOwner() {
            return GnssAntennaInfoProvider.this;
            return GnssAntennaInfoProvider.this;
        }
        }

        @Override
        protected IBinder getBinderFromKey(IBinder key) {
            return key;
        }

        @Override
        public String toString() {
            return mIdentity.toString();
        }
    }
    }


    private final GnssNative mGnssNative;
    private final GnssNative mGnssNative;
@@ -72,11 +92,6 @@ public class GnssAntennaInfoProvider extends
        return mAntennaInfos;
        return mAntennaInfos;
    }
    }


    @Override
    public String getTag() {
        return TAG;
    }

    public boolean isSupported() {
    public boolean isSupported() {
        return mGnssNative.isAntennaInfoSupported();
        return mGnssNative.isAntennaInfoSupported();
    }
    }
Loading