From 204b133883a57daad41500d4502e45ab88b7d35c Mon Sep 17 00:00:00 2001 From: Soonil Nagarkar Date: Tue, 2 Feb 2021 15:29:23 -0800 Subject: [PATCH] Reduce LocationManager memory usage Move GNSS listener overhead into statics so it's shared per process, and do not instantiate by default. Previously GNSS requests were multiplexed by LocationManager instance, this is now removed. This could mean additional binder overhead, but it's pretty unlikely that this feature was in active use anywhere, as in any large application that has multiple gnss requests, they are usually coming from different contexts (and thus LocationManager instances) and would not have seen the benefit of multiplexing anyways. Bug: 179261871 Test: atest CtsLocationFineTestCases Change-Id: I8b7a41c13dfe219c326b1e519483d89d2b721ce5 --- .../internal/listeners/ListenerTransport.java | 52 +- .../listeners/ListenerTransportManager.java | 97 +++ .../ListenerTransportMultiplexer.java | 258 ------- .../listeners/RequestListenerTransport.java | 45 -- .../ListenerTransportMultiplexerTest.java | 194 ----- .../android/location/LocationManager.java | 700 +++++++++--------- 6 files changed, 480 insertions(+), 866 deletions(-) create mode 100644 core/java/com/android/internal/listeners/ListenerTransportManager.java delete mode 100644 core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java delete mode 100644 core/java/com/android/internal/listeners/RequestListenerTransport.java delete mode 100644 core/tests/coretests/src/com/android/internal/listeners/ListenerTransportMultiplexerTest.java diff --git a/core/java/com/android/internal/listeners/ListenerTransport.java b/core/java/com/android/internal/listeners/ListenerTransport.java index 9d6210e5dcc83..1a6870c138f48 100644 --- a/core/java/com/android/internal/listeners/ListenerTransport.java +++ b/core/java/com/android/internal/listeners/ListenerTransport.java @@ -16,54 +16,43 @@ package com.android.internal.listeners; - -import android.annotation.NonNull; import android.annotation.Nullable; -import com.android.internal.util.Preconditions; - import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; /** - * A listener registration object which holds data associated with a listener, such the executor - * the listener should run on. + * A listener transport object which can run listener operations on an executor. * * @param listener type */ -public class ListenerTransport { - - private final Executor mExecutor; - - private volatile @Nullable TListener mListener; - - protected ListenerTransport(@NonNull Executor executor, @NonNull TListener listener) { - Preconditions.checkArgument(executor != null, "invalid null executor"); - Preconditions.checkArgument(listener != null, "invalid null listener/callback"); - mExecutor = executor; - mListener = listener; - } +public interface ListenerTransport { /** - * Prevents any listener invocations that happen-after this call. + * Should return a valid listener until {@link #unregister()} is invoked, and must return + * null after that. Recommended (but not required) that this is implemented via a volatile + * variable. */ - public final void unregister() { - mListener = null; - } + @Nullable TListener getListener(); + + /** + * Must be implemented so that {@link #getListener()} returns null after this is invoked. + */ + void unregister(); /** * Executes the given operation for the listener. */ - public final void execute(@NonNull Consumer operation) { + default void execute(Executor executor, Consumer operation) { Objects.requireNonNull(operation); - if (mListener == null) { + if (getListener() == null) { return; } - mExecutor.execute(() -> { - TListener listener = mListener; + executor.execute(() -> { + TListener listener = getListener(); if (listener == null) { return; } @@ -71,15 +60,4 @@ public class ListenerTransport { operation.accept(listener); }); } - - @Override - public final boolean equals(Object obj) { - // intentionally bound to reference equality so removal works as expected - return this == obj; - } - - @Override - public final int hashCode() { - return super.hashCode(); - } } diff --git a/core/java/com/android/internal/listeners/ListenerTransportManager.java b/core/java/com/android/internal/listeners/ListenerTransportManager.java new file mode 100644 index 0000000000000..0d5d1b7b53ffc --- /dev/null +++ b/core/java/com/android/internal/listeners/ListenerTransportManager.java @@ -0,0 +1,97 @@ +/* + * 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.internal.listeners; + +import android.os.RemoteException; + +import com.android.internal.annotations.GuardedBy; + +import java.lang.ref.WeakReference; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * A listener transport manager which handles mappings between the client facing listener and system + * server facing transport. Supports transports which may be removed either from the client side or + * from the system server side without leaking memory. + * + * @param > transport type + */ +public abstract class ListenerTransportManager> { + + @GuardedBy("mRegistrations") + private final Map> mRegistrations; + + protected ListenerTransportManager() { + // using weakhashmap means that the transport may be GCed if the server drops its reference, + // and thus the listener may be GCed as well if the client drops that reference. if the + // server will never drop a reference without warning (ie, transport removal may only be + // initiated from the client side), then arraymap or similar may be used without fear of + // memory leaks. + mRegistrations = new WeakHashMap<>(); + } + + /** + * Adds a new transport with the given listener key. + */ + public final void addListener(Object key, TTransport transport) { + try { + synchronized (mRegistrations) { + // ordering of operations is important so that if an error occurs at any point we + // are left in a reasonable state + registerTransport(transport); + WeakReference oldTransportRef = mRegistrations.put(key, + new WeakReference<>(transport)); + if (oldTransportRef != null) { + TTransport oldTransport = oldTransportRef.get(); + if (oldTransport != null) { + oldTransport.unregister(); + unregisterTransport(oldTransport); + } + } + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Removes the transport with the given listener key. + */ + public final void removeListener(Object key) { + try { + synchronized (mRegistrations) { + // ordering of operations is important so that if an error occurs at any point we + // are left in a reasonable state + WeakReference transportRef = mRegistrations.remove(key); + if (transportRef != null) { + TTransport transport = transportRef.get(); + if (transport != null) { + transport.unregister(); + unregisterTransport(transport); + } + } + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + protected abstract void registerTransport(TTransport transport) throws RemoteException; + + protected abstract void unregisterTransport(TTransport transport) throws RemoteException; +} diff --git a/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java b/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java deleted file mode 100644 index fc1d69f570ad6..0000000000000 --- a/core/java/com/android/internal/listeners/ListenerTransportMultiplexer.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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.internal.listeners; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Build; -import android.os.RemoteException; -import android.util.ArrayMap; -import android.util.IndentingPrintWriter; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; - -import java.io.FileDescriptor; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -/** - * A listener multiplexer designed for use by client-side code. This class ensures that listeners - * are never invoked while a lock is held. This class is only useful for multiplexing listeners - - * if all client listeners can be combined into a single server request, and all server results will - * be delivered to all clients. - * - * By default, the multiplexer will replace requests on the server simply by registering the new - * request and trusting the server to know this is replacing the old request. If the server needs to - * have the old request unregistered first, subclasses should override - * {@link #reregisterWithServer(Object, Object)}. - * - * @param listener request type, may be Void - * @param listener type - */ -public abstract class ListenerTransportMultiplexer { - - private final Object mLock = new Object(); - - @GuardedBy("mLock") - private ArrayMap> mRegistrations = - new ArrayMap<>(); - - @GuardedBy("mLock") - private boolean mServiceRegistered = false; - - @GuardedBy("mLock") - private TRequest mCurrentRequest; - - /** - * Should be implemented to register the given merged request with the server. - * - * @see #reregisterWithServer(Object, Object) - */ - protected abstract void registerWithServer(TRequest mergedRequest) throws RemoteException; - - /** - * Invoked when the server already has a request registered, and it is being replaced with a new - * request. The default implementation simply registers the new request, trusting the server to - * overwrite the old request. - */ - protected void reregisterWithServer(TRequest oldMergedRequest, TRequest mergedRequest) - throws RemoteException { - registerWithServer(mergedRequest); - } - - /** - * Should be implemented to unregister from the server. - */ - protected abstract void unregisterWithServer() throws RemoteException; - - /** - * Called in order to generate a merged request from the given requests. The list of requests - * will never be empty. - */ - protected @Nullable TRequest mergeRequests(Collection requests) { - if (Build.IS_DEBUGGABLE) { - for (TRequest request : requests) { - // if using non-null requests then implementations must override this method - Preconditions.checkState(request == null); - } - } - - return null; - } - - /** - * Adds a new listener with no request, using the listener as the key. - */ - public void addListener(@NonNull TListener listener, @NonNull Executor executor) { - addListener(listener, null, listener, executor); - } - - /** - * Adds a new listener with the given request, using the listener as the key. - */ - public void addListener(@Nullable TRequest request, @NonNull TListener listener, - @NonNull Executor executor) { - addListener(listener, request, listener, executor); - } - - /** - * Adds a new listener with the given request using a custom key. - */ - public void addListener(@NonNull Object key, @Nullable TRequest request, - @NonNull TListener listener, @NonNull Executor executor) { - Objects.requireNonNull(key); - RequestListenerTransport registration = - new RequestListenerTransport<>(request, executor, listener); - - synchronized (mLock) { - ArrayMap> newRegistrations = - new ArrayMap<>(mRegistrations.size() + 1); - newRegistrations.putAll(mRegistrations); - RequestListenerTransport old = newRegistrations.put(key, - registration); - mRegistrations = newRegistrations; - - if (old != null) { - old.unregister(); - } - - updateService(); - } - } - - /** - * Removes the listener with the given key. - */ - public void removeListener(@NonNull Object key) { - Objects.requireNonNull(key); - - synchronized (mLock) { - if (!mRegistrations.containsKey(key)) { - return; - } - - ArrayMap> newRegistrations = - new ArrayMap<>(mRegistrations); - RequestListenerTransport old = newRegistrations.remove(key); - mRegistrations = newRegistrations; - - if (old != null) { - old.unregister(); - updateService(); - } - } - } - - private void updateService() { - synchronized (mLock) { - if (mRegistrations.isEmpty()) { - mCurrentRequest = null; - if (mServiceRegistered) { - try { - mServiceRegistered = false; - unregisterWithServer(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - return; - } - - ArrayList requests = new ArrayList<>(mRegistrations.size()); - for (int i = 0; i < mRegistrations.size(); i++) { - requests.add(mRegistrations.valueAt(i).getRequest()); - } - - TRequest merged = mergeRequests(requests); - if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) { - TRequest old = mCurrentRequest; - mCurrentRequest = null; - try { - if (mServiceRegistered) { - // if a remote exception is thrown the service should not be registered - mServiceRegistered = false; - reregisterWithServer(old, merged); - } else { - registerWithServer(merged); - } - mCurrentRequest = merged; - mServiceRegistered = true; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - } - } - - protected final void deliverToListeners(Consumer operation) { - ArrayMap> registrations; - synchronized (mLock) { - registrations = mRegistrations; - } - - try { - for (int i = 0; i < registrations.size(); i++) { - registrations.valueAt(i).execute(operation); - } - } finally { - onOperationFinished(operation); - } - } - - /** - * Invoked when an operation is finished. This method will always be called once for every call - * to {@link #deliverToListeners(Consumer)}, regardless of whether the operation encountered any - * error or failed to execute in any way for any listeners. - */ - protected void onOperationFinished(@NonNull Consumer operation) {} - - /** - * Dumps debug information. - */ - public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { - ArrayMap> registrations; - synchronized (mLock) { - registrations = mRegistrations; - - ipw.print("service: "); - if (mServiceRegistered) { - if (mCurrentRequest == null) { - ipw.print("request registered"); - } else { - ipw.print("request registered - " + mCurrentRequest); - } - } else { - ipw.print("unregistered"); - } - ipw.println(); - } - - if (!registrations.isEmpty()) { - ipw.println("listeners:"); - - ipw.increaseIndent(); - for (int i = 0; i < registrations.size(); i++) { - ipw.print(registrations.valueAt(i)); - } - ipw.decreaseIndent(); - } - } -} diff --git a/core/java/com/android/internal/listeners/RequestListenerTransport.java b/core/java/com/android/internal/listeners/RequestListenerTransport.java deleted file mode 100644 index 178de0647fd18..0000000000000 --- a/core/java/com/android/internal/listeners/RequestListenerTransport.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.internal.listeners; - -import android.annotation.Nullable; - -import java.util.concurrent.Executor; - -/** - * A listener transport with an associated request. - * - * @param request type - * @param listener type - */ -public class RequestListenerTransport extends ListenerTransport { - - private final @Nullable TRequest mRequest; - - protected RequestListenerTransport(@Nullable TRequest request, Executor executor, - TListener listener) { - super(executor, listener); - mRequest = request; - } - - /** - * Returns the request associated with this transport. - */ - public final @Nullable TRequest getRequest() { - return mRequest; - } -} diff --git a/core/tests/coretests/src/com/android/internal/listeners/ListenerTransportMultiplexerTest.java b/core/tests/coretests/src/com/android/internal/listeners/ListenerTransportMultiplexerTest.java deleted file mode 100644 index 127ecfb9dc391..0000000000000 --- a/core/tests/coretests/src/com/android/internal/listeners/ListenerTransportMultiplexerTest.java +++ /dev/null @@ -1,194 +0,0 @@ -/* - * 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.internal.listeners; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.os.Handler; -import android.os.Looper; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import junit.framework.TestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.Collection; -import java.util.Comparator; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicReference; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ListenerTransportMultiplexerTest extends TestCase { - - TestMultiplexer mMultiplexer; - - @Before - public void setUp() { - mMultiplexer = new TestMultiplexer(); - } - - @Test - public void testAdd() { - Runnable runnable = mock(Runnable.class); - - mMultiplexer.addListener(0, runnable, Runnable::run); - assertThat(mMultiplexer.mRegistered).isTrue(); - assertThat(mMultiplexer.mMergedRequest).isEqualTo(0); - - mMultiplexer.notifyListeners(); - verify(runnable, times(1)).run(); - } - - @Test - public void testAdd_Multiple() { - Runnable runnable1 = mock(Runnable.class); - Runnable runnable2 = mock(Runnable.class); - - mMultiplexer.addListener(0, runnable1, Runnable::run); - mMultiplexer.addListener(0, runnable2, Runnable::run); - - mMultiplexer.notifyListeners(); - verify(runnable1).run(); - verify(runnable2).run(); - } - - @Test - public void testRemove() { - Runnable runnable = mock(Runnable.class); - - mMultiplexer.addListener(0, runnable, Runnable::run); - mMultiplexer.removeListener(runnable); - assertThat(mMultiplexer.mRegistered).isFalse(); - - mMultiplexer.notifyListeners(); - verify(runnable, never()).run(); - } - - @Test - public void testRemove_Multiple() { - Runnable runnable1 = mock(Runnable.class); - Runnable runnable2 = mock(Runnable.class); - - mMultiplexer.addListener(0, runnable1, Runnable::run); - mMultiplexer.addListener(1, runnable2, Runnable::run); - mMultiplexer.removeListener(runnable1); - - mMultiplexer.notifyListeners(); - verify(runnable1, never()).run(); - verify(runnable2).run(); - } - - @Test - public void testMergeMultiple() { - Runnable runnable1 = mock(Runnable.class); - Runnable runnable2 = mock(Runnable.class); - Runnable runnable3 = mock(Runnable.class); - - mMultiplexer.addListener(0, runnable1, Runnable::run); - mMultiplexer.addListener(1, runnable2, Runnable::run); - assertThat(mMultiplexer.mMergedRequest).isEqualTo(1); - - mMultiplexer.notifyListeners(); - verify(runnable1, times(1)).run(); - verify(runnable2, times(1)).run(); - verify(runnable3, times(0)).run(); - - mMultiplexer.addListener(0, runnable3, Runnable::run); - assertThat(mMultiplexer.mMergedRequest).isEqualTo(1); - - mMultiplexer.notifyListeners(); - verify(runnable1, times(2)).run(); - verify(runnable2, times(2)).run(); - verify(runnable3, times(1)).run(); - - mMultiplexer.removeListener(runnable2); - assertThat(mMultiplexer.mMergedRequest).isEqualTo(0); - - mMultiplexer.notifyListeners(); - verify(runnable1, times(3)).run(); - verify(runnable2, times(2)).run(); - verify(runnable3, times(2)).run(); - - mMultiplexer.removeListener(runnable1); - mMultiplexer.removeListener(runnable3); - mMultiplexer.notifyListeners(); - verify(runnable1, times(3)).run(); - verify(runnable2, times(2)).run(); - verify(runnable3, times(2)).run(); - } - - @Test(timeout = 5000) - public void testReentrancy() { - AtomicReference runnable = new AtomicReference<>(); - runnable.set(() -> mMultiplexer.removeListener(runnable.get())); - - mMultiplexer.addListener(0, runnable.get(), command -> { - CountDownLatch latch = new CountDownLatch(1); - new Handler(Looper.getMainLooper()).post(() -> { - command.run(); - latch.countDown(); - }); - try { - latch.await(); - } catch (InterruptedException e) { - throw new AssertionError(e); - } - }); - - mMultiplexer.notifyListeners(); - assertThat(mMultiplexer.mRegistered).isFalse(); - } - - private static class TestMultiplexer extends ListenerTransportMultiplexer { - - boolean mRegistered; - int mMergedRequest; - - TestMultiplexer() { - } - - public void notifyListeners() { - deliverToListeners(Runnable::run); - } - - @Override - protected void registerWithServer(Integer mergedRequest) { - mRegistered = true; - mMergedRequest = mergedRequest; - } - - @Override - protected void unregisterWithServer() { - mRegistered = false; - } - - @Override - protected Integer mergeRequests(Collection requests) { - return requests.stream().max(Comparator.naturalOrder()).get(); - } - } -} diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index fdb044d0dcf6a..d8008d5b34f44 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.Manifest.permission.LOCATION_HARDWARE; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; +import static android.location.GpsStatus.GPS_EVENT_STARTED; import static android.location.LocationRequest.createFromDeprecatedCriteria; import static android.location.LocationRequest.createFromDeprecatedProvider; @@ -43,7 +44,6 @@ import android.app.PropertyInvalidatedCache; import android.compat.Compatibility; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; -import android.compat.annotation.UnsupportedAppUsage; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -60,16 +60,17 @@ import android.os.IRemoteCallback; import android.os.Looper; import android.os.Process; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import com.android.internal.annotations.GuardedBy; import com.android.internal.listeners.ListenerExecutor; -import com.android.internal.listeners.ListenerTransportMultiplexer; +import com.android.internal.listeners.ListenerTransport; +import com.android.internal.listeners.ListenerTransportManager; import com.android.internal.util.Preconditions; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -398,20 +399,40 @@ public class LocationManager { private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000; + private static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY = + "cache_key.location_enabled"; + + private static ILocationManager getService() throws RemoteException { + try { + return ILocationManager.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE)); + } catch (ServiceManager.ServiceNotFoundException e) { + throw new RemoteException(e); + } + } + @GuardedBy("sLocationListeners") private static final WeakHashMap> sLocationListeners = new WeakHashMap<>(); - final Context mContext; + // allows lazy instantiation since most processes do not use GNSS APIs + private static class GnssLazyLoader { + static final GnssStatusTransportManager sGnssStatusListeners = + new GnssStatusTransportManager(); + static final GnssNmeaTransportManager sGnssNmeaListeners = + new GnssNmeaTransportManager(); + static final GnssMeasurementsTransportManager sGnssMeasurementsListeners = + new GnssMeasurementsTransportManager(); + static final GnssAntennaTransportManager sGnssAntennaInfoListeners = + new GnssAntennaTransportManager(); + static final GnssNavigationTransportManager sGnssNavigationListeners = + new GnssNavigationTransportManager(); + } - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link " - + "LocationManager}") - final ILocationManager mService; + private final Context mContext; + private final ILocationManager mService; - private final Object mLock = new Object(); - - @GuardedBy("mLock") - private PropertyInvalidatedCache mLocationEnabledCache = + private volatile PropertyInvalidatedCache mLocationEnabledCache = new PropertyInvalidatedCache( 4, CACHE_KEY_LOCATION_ENABLED_PROPERTY) { @@ -425,68 +446,12 @@ public class LocationManager { } }; - @GuardedBy("mLock") - @Nullable private GnssStatusTransportMultiplexer mGnssStatusTransportMultiplexer; - @GuardedBy("mLock") - @Nullable private GnssNmeaTransportMultiplexer mGnssNmeaTransportMultiplexer; - @GuardedBy("mLock") - @Nullable private GnssMeasurementsTransportMultiplexer mGnssMeasurementsTransportMultiplexer; - @GuardedBy("mLock") - @Nullable private GnssNavigationTransportMultiplexer mGnssNavigationTransportMultiplexer; - @GuardedBy("mLock") - @Nullable private GnssAntennaInfoTransportMultiplexer mGnssAntennaInfoTransportMultiplexer; - /** * @hide */ public LocationManager(@NonNull Context context, @NonNull ILocationManager service) { - mService = service; - mContext = context; - } - - private GnssStatusTransportMultiplexer getGnssStatusTransportMultiplexer() { - synchronized (mLock) { - if (mGnssStatusTransportMultiplexer == null) { - mGnssStatusTransportMultiplexer = new GnssStatusTransportMultiplexer(); - } - return mGnssStatusTransportMultiplexer; - } - } - - private GnssNmeaTransportMultiplexer getGnssNmeaTransportMultiplexer() { - synchronized (mLock) { - if (mGnssNmeaTransportMultiplexer == null) { - mGnssNmeaTransportMultiplexer = new GnssNmeaTransportMultiplexer(); - } - return mGnssNmeaTransportMultiplexer; - } - } - - private GnssMeasurementsTransportMultiplexer getGnssMeasurementsTransportMultiplexer() { - synchronized (mLock) { - if (mGnssMeasurementsTransportMultiplexer == null) { - mGnssMeasurementsTransportMultiplexer = new GnssMeasurementsTransportMultiplexer(); - } - return mGnssMeasurementsTransportMultiplexer; - } - } - - private GnssNavigationTransportMultiplexer getGnssNavigationTransportMultiplexer() { - synchronized (mLock) { - if (mGnssNavigationTransportMultiplexer == null) { - mGnssNavigationTransportMultiplexer = new GnssNavigationTransportMultiplexer(); - } - return mGnssNavigationTransportMultiplexer; - } - } - - private GnssAntennaInfoTransportMultiplexer getGnssAntennaInfoTransportMultiplexer() { - synchronized (mLock) { - if (mGnssAntennaInfoTransportMultiplexer == null) { - mGnssAntennaInfoTransportMultiplexer = new GnssAntennaInfoTransportMultiplexer(); - } - return mGnssAntennaInfoTransportMultiplexer; - } + mContext = Objects.requireNonNull(context); + mService = Objects.requireNonNull(service); } /** @@ -627,10 +592,9 @@ public class LocationManager { */ @SystemApi public boolean isLocationEnabledForUser(@NonNull UserHandle userHandle) { - synchronized (mLock) { - if (mLocationEnabledCache != null) { - return mLocationEnabledCache.query(userHandle.getIdentifier()); - } + PropertyInvalidatedCache cache = mLocationEnabledCache; + if (cache != null) { + return cache.query(userHandle.getIdentifier()); } // fallback if cache is disabled @@ -2264,9 +2228,8 @@ public class LocationManager { "GpsStatus APIs not supported, please use GnssStatus APIs instead"); } - GnssStatusTransportMultiplexer multiplexer = getGnssStatusTransportMultiplexer(); - GnssStatus gnssStatus = multiplexer.getGnssStatus(); - int ttff = multiplexer.getTtff(); + GnssStatus gnssStatus = GpsStatusTransport.sGnssStatus; + int ttff = GpsStatusTransport.sTtff; if (gnssStatus != null) { if (status == null) { status = GpsStatus.create(gnssStatus, ttff); @@ -2299,8 +2262,8 @@ public class LocationManager { "GpsStatus APIs not supported, please use GnssStatus APIs instead"); } - getGnssStatusTransportMultiplexer().addListener(listener, - new HandlerExecutor(new Handler())); + GnssLazyLoader.sGnssStatusListeners.addListener(listener, + new GpsStatusTransport(new HandlerExecutor(new Handler()), mContext, listener)); return true; } @@ -2319,7 +2282,7 @@ public class LocationManager { "GpsStatus APIs not supported, please use GnssStatus APIs instead"); } - getGnssStatusTransportMultiplexer().removeListener(listener); + GnssLazyLoader.sGnssStatusListeners.removeListener(listener); } /** @@ -2382,7 +2345,8 @@ public class LocationManager { public boolean registerGnssStatusCallback( @NonNull @CallbackExecutor Executor executor, @NonNull GnssStatus.Callback callback) { - getGnssStatusTransportMultiplexer().addListener(callback, executor); + GnssLazyLoader.sGnssStatusListeners.addListener(callback, + new GnssStatusTransport(executor, mContext, callback)); return true; } @@ -2392,7 +2356,7 @@ public class LocationManager { * @param callback GNSS status callback object to remove */ public void unregisterGnssStatusCallback(@NonNull GnssStatus.Callback callback) { - getGnssStatusTransportMultiplexer().removeListener(callback); + GnssLazyLoader.sGnssStatusListeners.removeListener(callback); } /** @@ -2472,7 +2436,8 @@ public class LocationManager { public boolean addNmeaListener( @NonNull @CallbackExecutor Executor executor, @NonNull OnNmeaMessageListener listener) { - getGnssNmeaTransportMultiplexer().addListener(listener, executor); + GnssLazyLoader.sGnssNmeaListeners.addListener(listener, + new GnssNmeaTransport(executor, mContext, listener)); return true; } @@ -2482,7 +2447,7 @@ public class LocationManager { * @param listener a {@link OnNmeaMessageListener} object to remove */ public void removeNmeaListener(@NonNull OnNmeaMessageListener listener) { - getGnssNmeaTransportMultiplexer().removeListener(listener); + GnssLazyLoader.sGnssNmeaListeners.removeListener(listener); } /** @@ -2598,10 +2563,8 @@ public class LocationManager { @NonNull GnssRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull GnssMeasurementsEvent.Callback callback) { - Preconditions.checkArgument(request != null, "invalid null request"); - getGnssMeasurementsTransportMultiplexer().addListener(request.toGnssMeasurementRequest(), - callback, executor); - return true; + return registerGnssMeasurementsCallback(request.toGnssMeasurementRequest(), executor, + callback); } /** @@ -2623,8 +2586,8 @@ public class LocationManager { @NonNull GnssMeasurementRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull GnssMeasurementsEvent.Callback callback) { - Preconditions.checkArgument(request != null, "invalid null request"); - getGnssMeasurementsTransportMultiplexer().addListener(request, callback, executor); + GnssLazyLoader.sGnssMeasurementsListeners.addListener(callback, + new GnssMeasurementsTransport(executor, mContext, request, callback)); return true; } @@ -2656,7 +2619,7 @@ public class LocationManager { */ public void unregisterGnssMeasurementsCallback( @NonNull GnssMeasurementsEvent.Callback callback) { - getGnssMeasurementsTransportMultiplexer().removeListener(callback); + GnssLazyLoader.sGnssMeasurementsListeners.removeListener(callback); } /** @@ -2681,7 +2644,8 @@ public class LocationManager { public boolean registerAntennaInfoListener( @NonNull @CallbackExecutor Executor executor, @NonNull GnssAntennaInfo.Listener listener) { - getGnssAntennaInfoTransportMultiplexer().addListener(listener, executor); + GnssLazyLoader.sGnssAntennaInfoListeners.addListener(listener, + new GnssAntennaInfoTransport(executor, mContext, listener)); return true; } @@ -2694,7 +2658,7 @@ public class LocationManager { */ @Deprecated public void unregisterAntennaInfoListener(@NonNull GnssAntennaInfo.Listener listener) { - getGnssAntennaInfoTransportMultiplexer().removeListener(listener); + GnssLazyLoader.sGnssAntennaInfoListeners.removeListener(listener); } /** @@ -2784,7 +2748,8 @@ public class LocationManager { public boolean registerGnssNavigationMessageCallback( @NonNull @CallbackExecutor Executor executor, @NonNull GnssNavigationMessage.Callback callback) { - getGnssNavigationTransportMultiplexer().addListener(callback, executor); + GnssLazyLoader.sGnssNavigationListeners.addListener(callback, + new GnssNavigationTransport(executor, mContext, callback)); return true; } @@ -2795,7 +2760,7 @@ public class LocationManager { */ public void unregisterGnssNavigationMessageCallback( @NonNull GnssNavigationMessage.Callback callback) { - getGnssNavigationTransportMultiplexer().removeListener(callback); + GnssLazyLoader.sGnssNavigationListeners.removeListener(callback); } /** @@ -2904,6 +2869,89 @@ public class LocationManager { } } + private static class GnssStatusTransportManager extends + ListenerTransportManager { + + @Override + protected void registerTransport(GnssStatusTransport transport) + throws RemoteException { + getService().registerGnssStatusCallback(transport, transport.getPackage(), + transport.getAttributionTag()); + } + + @Override + protected void unregisterTransport(GnssStatusTransport transport) + throws RemoteException { + getService().unregisterGnssStatusCallback(transport); + } + } + + private static class GnssNmeaTransportManager extends + ListenerTransportManager { + + @Override + protected void registerTransport(GnssNmeaTransport transport) + throws RemoteException { + getService().registerGnssNmeaCallback(transport, transport.getPackage(), + transport.getAttributionTag()); + } + + @Override + protected void unregisterTransport(GnssNmeaTransport transport) + throws RemoteException { + getService().unregisterGnssNmeaCallback(transport); + } + } + + private static class GnssMeasurementsTransportManager extends + ListenerTransportManager { + + @Override + protected void registerTransport(GnssMeasurementsTransport transport) + throws RemoteException { + getService().addGnssMeasurementsListener(transport.getRequest(), transport, + transport.getPackage(), transport.getAttributionTag()); + } + + @Override + protected void unregisterTransport(GnssMeasurementsTransport transport) + throws RemoteException { + getService().removeGnssMeasurementsListener(transport); + } + } + + private static class GnssAntennaTransportManager extends + ListenerTransportManager { + + @Override + protected void registerTransport(GnssAntennaInfoTransport transport) { + transport.getContext().registerReceiver(transport, + new IntentFilter(ACTION_GNSS_ANTENNA_INFOS_CHANGED)); + } + + @Override + protected void unregisterTransport(GnssAntennaInfoTransport transport) { + transport.getContext().unregisterReceiver(transport); + } + } + + private static class GnssNavigationTransportManager extends + ListenerTransportManager { + + @Override + protected void registerTransport(GnssNavigationTransport transport) + throws RemoteException { + getService().addGnssNavigationMessageListener(transport, + transport.getPackage(), transport.getAttributionTag()); + } + + @Override + protected void unregisterTransport(GnssNavigationTransport transport) + throws RemoteException { + getService().removeGnssNavigationMessageListener(transport); + } + } + private static class GetCurrentLocationTransport extends ILocationCallback.Stub implements ListenerExecutor, CancellationSignal.OnCancelListener { @@ -3018,7 +3066,7 @@ public class LocationManager { @Override public void onStarted() { - mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED); + mGpsListener.onGpsStatusChanged(GPS_EVENT_STARTED); } @Override @@ -3037,273 +3085,269 @@ public class LocationManager { } } - private class GnssStatusTransportMultiplexer extends - ListenerTransportMultiplexer { + private static class GnssStatusTransport extends IGnssStatusListener.Stub implements + ListenerTransport { - private @Nullable IGnssStatusListener mListenerTransport; + private final Executor mExecutor; + private final String mPackageName; + private final String mAttributionTag; - volatile @Nullable GnssStatus mGnssStatus; - volatile int mTtff; + private volatile @Nullable GnssStatus.Callback mListener; - GnssStatusTransportMultiplexer() {} - - public GnssStatus getGnssStatus() { - return mGnssStatus; + GnssStatusTransport(Executor executor, Context context, GnssStatus.Callback listener) { + Preconditions.checkArgument(executor != null, "invalid null executor"); + Preconditions.checkArgument(listener != null, "invalid null callback"); + mExecutor = executor; + mPackageName = context.getPackageName(); + mAttributionTag = context.getAttributionTag(); + mListener = listener; } - public int getTtff() { - return mTtff; + public String getPackage() { + return mPackageName; } - public void addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor) { - addListener(listener, null, new GpsAdapter(listener), executor); + public String getAttributionTag() { + return mAttributionTag; } @Override - protected void registerWithServer(Void ignored) throws RemoteException { - IGnssStatusListener transport = mListenerTransport; - if (transport == null) { - transport = new GnssStatusListener(); - } - - // if a remote exception is thrown the transport should not be set - mListenerTransport = null; - mService.registerGnssStatusCallback(transport, mContext.getPackageName(), - mContext.getAttributionTag()); - mListenerTransport = transport; + public void unregister() { + mListener = null; } @Override - protected void unregisterWithServer() throws RemoteException { - if (mListenerTransport != null) { - IGnssStatusListener transport = mListenerTransport; - mListenerTransport = null; - mService.unregisterGnssStatusCallback(transport); - } + public @Nullable GnssStatus.Callback getListener() { + return mListener; } - private class GnssStatusListener extends IGnssStatusListener.Stub { + @Override + public void onGnssStarted() { + execute(mExecutor, GnssStatus.Callback::onStarted); + } - GnssStatusListener() {} + @Override + public void onGnssStopped() { + execute(mExecutor, GnssStatus.Callback::onStopped); + } - @Override - public void onGnssStarted() { - deliverToListeners(GnssStatus.Callback::onStarted); - } + @Override + public void onFirstFix(int ttff) { + execute(mExecutor, listener -> listener.onFirstFix(ttff)); - @Override - public void onGnssStopped() { - deliverToListeners(GnssStatus.Callback::onStopped); - } + } - @Override - public void onFirstFix(int ttff) { - mTtff = ttff; - deliverToListeners(callback -> callback.onFirstFix(ttff)); - } + @Override + public void onSvStatusChanged(GnssStatus gnssStatus) { + execute(mExecutor, listener -> listener.onSatelliteStatusChanged(gnssStatus)); + } + } - @Override - public void onSvStatusChanged(GnssStatus gnssStatus) { - mGnssStatus = gnssStatus; - deliverToListeners(callback -> callback.onSatelliteStatusChanged(gnssStatus)); + private static class GpsStatusTransport extends GnssStatusTransport { + + static volatile int sTtff; + static volatile GnssStatus sGnssStatus; + + GpsStatusTransport(Executor executor, Context context, GpsStatus.Listener listener) { + super(executor, context, new GpsAdapter(listener)); + } + + @Override + public void onFirstFix(int ttff) { + sTtff = ttff; + super.onFirstFix(ttff); + } + + @Override + public void onSvStatusChanged(GnssStatus gnssStatus) { + sGnssStatus = gnssStatus; + super.onSvStatusChanged(gnssStatus); + } + } + + private static class GnssNmeaTransport extends IGnssNmeaListener.Stub implements + ListenerTransport { + + private final Executor mExecutor; + private final String mPackageName; + private final String mAttributionTag; + + private volatile @Nullable OnNmeaMessageListener mListener; + + GnssNmeaTransport(Executor executor, Context context, OnNmeaMessageListener listener) { + Preconditions.checkArgument(executor != null, "invalid null executor"); + Preconditions.checkArgument(listener != null, "invalid null listener"); + mExecutor = executor; + mPackageName = context.getPackageName(); + mAttributionTag = context.getAttributionTag(); + mListener = listener; + } + + public String getPackage() { + return mPackageName; + } + + public String getAttributionTag() { + return mAttributionTag; + } + + @Override + public void unregister() { + mListener = null; + } + + @Override + public @Nullable OnNmeaMessageListener getListener() { + return mListener; + } + + @Override + public void onNmeaReceived(long timestamp, String nmea) { + execute(mExecutor, callback -> callback.onNmeaMessage(nmea, timestamp)); + } + } + + private static class GnssMeasurementsTransport extends IGnssMeasurementsListener.Stub implements + ListenerTransport { + + private final Executor mExecutor; + private final String mPackageName; + private final String mAttributionTag; + private final GnssMeasurementRequest mRequest; + + private volatile @Nullable GnssMeasurementsEvent.Callback mListener; + + GnssMeasurementsTransport(Executor executor, Context context, + GnssMeasurementRequest request, GnssMeasurementsEvent.Callback listener) { + Preconditions.checkArgument(executor != null, "invalid null executor"); + Preconditions.checkArgument(listener != null, "invalid null callback"); + Preconditions.checkArgument(request != null, "invalid null request"); + mExecutor = executor; + mPackageName = context.getPackageName(); + mAttributionTag = context.getAttributionTag(); + mRequest = request; + mListener = listener; + } + + public String getPackage() { + return mPackageName; + } + + public String getAttributionTag() { + return mAttributionTag; + } + + public GnssMeasurementRequest getRequest() { + return mRequest; + } + + @Override + public void unregister() { + mListener = null; + } + + @Override + public @Nullable GnssMeasurementsEvent.Callback getListener() { + return mListener; + } + + @Override + public void onGnssMeasurementsReceived(GnssMeasurementsEvent event) { + execute(mExecutor, callback -> callback.onGnssMeasurementsReceived(event)); + } + + @Override + public void onStatusChanged(int status) { + execute(mExecutor, callback -> callback.onStatusChanged(status)); + } + } + + private static class GnssAntennaInfoTransport extends BroadcastReceiver implements + ListenerTransport { + + private final Executor mExecutor; + private final Context mContext; + + private volatile @Nullable GnssAntennaInfo.Listener mListener; + + GnssAntennaInfoTransport(Executor executor, Context context, + GnssAntennaInfo.Listener listener) { + Preconditions.checkArgument(executor != null, "invalid null executor"); + Preconditions.checkArgument(listener != null, "invalid null listener"); + mExecutor = executor; + mContext = context; + mListener = listener; + } + + public Context getContext() { + return mContext; + } + + @Override + public void unregister() { + mListener = null; + } + + @Override + public @Nullable GnssAntennaInfo.Listener getListener() { + return mListener; + } + + @Override + public void onReceive(Context context, Intent intent) { + ArrayList infos = intent.getParcelableArrayListExtra( + EXTRA_GNSS_ANTENNA_INFOS); + if (infos != null) { + execute(mExecutor, callback -> callback.onGnssAntennaInfoReceived(infos)); } } } - private class GnssNmeaTransportMultiplexer extends - ListenerTransportMultiplexer { + private static class GnssNavigationTransport extends IGnssNavigationMessageListener.Stub + implements ListenerTransport { - private @Nullable IGnssNmeaListener mListenerTransport; + private final Executor mExecutor; + private final String mPackageName; + private final String mAttributionTag; - GnssNmeaTransportMultiplexer() {} + private volatile @Nullable GnssNavigationMessage.Callback mListener; - public void addListener(@NonNull OnNmeaMessageListener listener, - @NonNull Executor executor) { - addListener(listener, null, listener, executor); + GnssNavigationTransport(Executor executor, Context context, + GnssNavigationMessage.Callback listener) { + Preconditions.checkArgument(executor != null, "invalid null executor"); + Preconditions.checkArgument(listener != null, "invalid null callback"); + mExecutor = executor; + mPackageName = context.getPackageName(); + mAttributionTag = context.getAttributionTag(); + mListener = listener; + } + + public String getPackage() { + return mPackageName; + } + + public String getAttributionTag() { + return mAttributionTag; } @Override - protected void registerWithServer(Void ignored) throws RemoteException { - IGnssNmeaListener transport = mListenerTransport; - if (transport == null) { - transport = new GnssNmeaListener(); - } - - // if a remote exception is thrown the transport should not be set - mListenerTransport = null; - mService.registerGnssNmeaCallback(transport, mContext.getPackageName(), - mContext.getAttributionTag()); - mListenerTransport = transport; + public void unregister() { + mListener = null; } @Override - protected void unregisterWithServer() throws RemoteException { - if (mListenerTransport != null) { - IGnssNmeaListener transport = mListenerTransport; - mListenerTransport = null; - mService.unregisterGnssNmeaCallback(transport); - } - } - - private class GnssNmeaListener extends IGnssNmeaListener.Stub { - - GnssNmeaListener() {} - - @Override - public void onNmeaReceived(long timestamp, String nmea) { - deliverToListeners(callback -> callback.onNmeaMessage(nmea, timestamp)); - } - } - } - - private class GnssMeasurementsTransportMultiplexer extends - ListenerTransportMultiplexer { - - private @Nullable IGnssMeasurementsListener mListenerTransport; - - GnssMeasurementsTransportMultiplexer() {} - - @Override - protected void registerWithServer(GnssMeasurementRequest request) throws RemoteException { - IGnssMeasurementsListener transport = mListenerTransport; - if (transport == null) { - transport = new GnssMeasurementsListener(); - } - - // if a remote exception is thrown the transport should not be set - mListenerTransport = null; - mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(), - mContext.getAttributionTag()); - mListenerTransport = transport; + public @Nullable GnssNavigationMessage.Callback getListener() { + return mListener; } @Override - protected void unregisterWithServer() throws RemoteException { - if (mListenerTransport != null) { - IGnssMeasurementsListener transport = mListenerTransport; - mListenerTransport = null; - mService.removeGnssMeasurementsListener(transport); - } + public void onGnssNavigationMessageReceived(GnssNavigationMessage event) { + execute(mExecutor, listener -> listener.onGnssNavigationMessageReceived(event)); } @Override - protected GnssMeasurementRequest mergeRequests( - Collection requests) { - GnssMeasurementRequest.Builder builder = new GnssMeasurementRequest.Builder(); - for (GnssMeasurementRequest request : requests) { - if (request.isFullTracking()) { - builder.setFullTracking(true); - } - if (request.isCorrelationVectorOutputsEnabled()) { - builder.setCorrelationVectorOutputsEnabled(true); - } - } - - return builder.build(); - } - - private class GnssMeasurementsListener extends IGnssMeasurementsListener.Stub { - - GnssMeasurementsListener() {} - - @Override - public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) { - deliverToListeners(callback -> callback.onGnssMeasurementsReceived(event)); - } - - @Override - public void onStatusChanged(int status) { - deliverToListeners(callback -> callback.onStatusChanged(status)); - } - } - } - - private class GnssNavigationTransportMultiplexer extends - ListenerTransportMultiplexer { - - @Nullable - private IGnssNavigationMessageListener mListenerTransport; - - GnssNavigationTransportMultiplexer() {} - - @Override - protected void registerWithServer(Void ignored) throws RemoteException { - IGnssNavigationMessageListener transport = mListenerTransport; - if (transport == null) { - transport = new GnssNavigationMessageListener(); - } - - // if a remote exception is thrown the transport should not be set - mListenerTransport = null; - mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(), - mContext.getAttributionTag()); - mListenerTransport = transport; - } - - @Override - protected void unregisterWithServer() throws RemoteException { - if (mListenerTransport != null) { - IGnssNavigationMessageListener transport = mListenerTransport; - mListenerTransport = null; - mService.removeGnssNavigationMessageListener(transport); - } - } - - private class GnssNavigationMessageListener extends IGnssNavigationMessageListener.Stub { - - GnssNavigationMessageListener() {} - - @Override - public void onGnssNavigationMessageReceived(GnssNavigationMessage event) { - deliverToListeners(listener -> listener.onGnssNavigationMessageReceived(event)); - } - - @Override - public void onStatusChanged(int status) { - deliverToListeners(listener -> listener.onStatusChanged(status)); - } - } - } - - private class GnssAntennaInfoTransportMultiplexer extends - ListenerTransportMultiplexer { - - private @Nullable BroadcastReceiver mListenerTransport; - - GnssAntennaInfoTransportMultiplexer() {} - - @Override - protected void registerWithServer(Void ignored) { - if (mListenerTransport == null) { - // if an exception is thrown the transport should not be set - BroadcastReceiver transport = new GnssAntennaInfoReceiver(); - mContext.registerReceiver(transport, - new IntentFilter(ACTION_GNSS_ANTENNA_INFOS_CHANGED)); - mListenerTransport = transport; - } - } - - @Override - protected void unregisterWithServer() { - if (mListenerTransport != null) { - BroadcastReceiver transport = mListenerTransport; - mListenerTransport = null; - mContext.unregisterReceiver(transport); - } - } - - private class GnssAntennaInfoReceiver extends BroadcastReceiver { - - GnssAntennaInfoReceiver() {} - - @Override - public void onReceive(Context context, Intent intent) { - ArrayList infos = intent.getParcelableArrayListExtra( - EXTRA_GNSS_ANTENNA_INFOS); - if (infos != null) { - deliverToListeners(callback -> callback.onGnssAntennaInfoReceived(infos)); - } - } + public void onStatusChanged(int status) { + execute(mExecutor, listener -> listener.onStatusChanged(status)); } } @@ -3333,12 +3377,6 @@ public class LocationManager { } } - /** - * @hide - */ - private static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY = - "cache_key.location_enabled"; - /** * @hide */ @@ -3350,8 +3388,6 @@ public class LocationManager { * @hide */ public void disableLocalLocationEnabledCaches() { - synchronized (mLock) { - mLocationEnabledCache = null; - } + mLocationEnabledCache = null; } }