Merge "Reduce LocationManager memory usage" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
4c7e9d1a90
@@ -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 <TListener> listener type
|
||||
*/
|
||||
public class ListenerTransport<TListener> {
|
||||
|
||||
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<TListener> {
|
||||
|
||||
/**
|
||||
* 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<TListener> operation) {
|
||||
default void execute(Executor executor, Consumer<TListener> 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<TListener> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <TTransport>> transport type
|
||||
*/
|
||||
public abstract class ListenerTransportManager<TTransport extends ListenerTransport<?>> {
|
||||
|
||||
@GuardedBy("mRegistrations")
|
||||
private final Map<Object, WeakReference<TTransport>> 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<TTransport> 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<TTransport> 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;
|
||||
}
|
||||
@@ -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 <TRequest> listener request type, may be Void
|
||||
* @param <TListener> listener type
|
||||
*/
|
||||
public abstract class ListenerTransportMultiplexer<TRequest, TListener> {
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> 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<TRequest> 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<TRequest, TListener> registration =
|
||||
new RequestListenerTransport<>(request, executor, listener);
|
||||
|
||||
synchronized (mLock) {
|
||||
ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> newRegistrations =
|
||||
new ArrayMap<>(mRegistrations.size() + 1);
|
||||
newRegistrations.putAll(mRegistrations);
|
||||
RequestListenerTransport<TRequest, TListener> 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<Object, RequestListenerTransport<TRequest, TListener>> newRegistrations =
|
||||
new ArrayMap<>(mRegistrations);
|
||||
RequestListenerTransport<TRequest, TListener> 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<TRequest> 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<TListener> operation) {
|
||||
ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> 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<TListener> operation) {}
|
||||
|
||||
/**
|
||||
* Dumps debug information.
|
||||
*/
|
||||
public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
|
||||
ArrayMap<Object, RequestListenerTransport<TRequest, TListener>> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <TRequest> request type
|
||||
* @param <TListener> listener type
|
||||
*/
|
||||
public class RequestListenerTransport<TRequest, TListener> extends ListenerTransport<TListener> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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> 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<Integer, Runnable> {
|
||||
|
||||
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<Integer> requests) {
|
||||
return requests.stream().max(Comparator.naturalOrder()).get();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<LocationListener, WeakReference<LocationListenerTransport>>
|
||||
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<Integer, Boolean> mLocationEnabledCache =
|
||||
private volatile PropertyInvalidatedCache<Integer, Boolean> mLocationEnabledCache =
|
||||
new PropertyInvalidatedCache<Integer, Boolean>(
|
||||
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<Integer, Boolean> cache = mLocationEnabledCache;
|
||||
if (cache != null) {
|
||||
return cache.query(userHandle.getIdentifier());
|
||||
}
|
||||
|
||||
// fallback if cache is disabled
|
||||
@@ -2265,9 +2229,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);
|
||||
@@ -2300,8 +2263,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;
|
||||
}
|
||||
|
||||
@@ -2320,7 +2283,7 @@ public class LocationManager {
|
||||
"GpsStatus APIs not supported, please use GnssStatus APIs instead");
|
||||
}
|
||||
|
||||
getGnssStatusTransportMultiplexer().removeListener(listener);
|
||||
GnssLazyLoader.sGnssStatusListeners.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2383,7 +2346,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;
|
||||
}
|
||||
|
||||
@@ -2393,7 +2357,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2473,7 +2437,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;
|
||||
}
|
||||
|
||||
@@ -2483,7 +2448,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2599,10 +2564,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2624,8 +2587,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;
|
||||
}
|
||||
|
||||
@@ -2657,7 +2620,7 @@ public class LocationManager {
|
||||
*/
|
||||
public void unregisterGnssMeasurementsCallback(
|
||||
@NonNull GnssMeasurementsEvent.Callback callback) {
|
||||
getGnssMeasurementsTransportMultiplexer().removeListener(callback);
|
||||
GnssLazyLoader.sGnssMeasurementsListeners.removeListener(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2682,7 +2645,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;
|
||||
}
|
||||
|
||||
@@ -2695,7 +2659,7 @@ public class LocationManager {
|
||||
*/
|
||||
@Deprecated
|
||||
public void unregisterAntennaInfoListener(@NonNull GnssAntennaInfo.Listener listener) {
|
||||
getGnssAntennaInfoTransportMultiplexer().removeListener(listener);
|
||||
GnssLazyLoader.sGnssAntennaInfoListeners.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2785,7 +2749,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;
|
||||
}
|
||||
|
||||
@@ -2796,7 +2761,7 @@ public class LocationManager {
|
||||
*/
|
||||
public void unregisterGnssNavigationMessageCallback(
|
||||
@NonNull GnssNavigationMessage.Callback callback) {
|
||||
getGnssNavigationTransportMultiplexer().removeListener(callback);
|
||||
GnssLazyLoader.sGnssNavigationListeners.removeListener(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2905,6 +2870,89 @@ public class LocationManager {
|
||||
}
|
||||
}
|
||||
|
||||
private static class GnssStatusTransportManager extends
|
||||
ListenerTransportManager<GnssStatusTransport> {
|
||||
|
||||
@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<GnssNmeaTransport> {
|
||||
|
||||
@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<GnssMeasurementsTransport> {
|
||||
|
||||
@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<GnssAntennaInfoTransport> {
|
||||
|
||||
@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<GnssNavigationTransport> {
|
||||
|
||||
@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 {
|
||||
|
||||
@@ -3019,7 +3067,7 @@ public class LocationManager {
|
||||
|
||||
@Override
|
||||
public void onStarted() {
|
||||
mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
|
||||
mGpsListener.onGpsStatusChanged(GPS_EVENT_STARTED);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3038,273 +3086,269 @@ public class LocationManager {
|
||||
}
|
||||
}
|
||||
|
||||
private class GnssStatusTransportMultiplexer extends
|
||||
ListenerTransportMultiplexer<Void, GnssStatus.Callback> {
|
||||
private static class GnssStatusTransport extends IGnssStatusListener.Stub implements
|
||||
ListenerTransport<GnssStatus.Callback> {
|
||||
|
||||
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<OnNmeaMessageListener> {
|
||||
|
||||
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<GnssMeasurementsEvent.Callback> {
|
||||
|
||||
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<GnssAntennaInfo.Listener> {
|
||||
|
||||
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<GnssAntennaInfo> infos = intent.getParcelableArrayListExtra(
|
||||
EXTRA_GNSS_ANTENNA_INFOS);
|
||||
if (infos != null) {
|
||||
execute(mExecutor, callback -> callback.onGnssAntennaInfoReceived(infos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GnssNmeaTransportMultiplexer extends
|
||||
ListenerTransportMultiplexer<Void, OnNmeaMessageListener> {
|
||||
private static class GnssNavigationTransport extends IGnssNavigationMessageListener.Stub
|
||||
implements ListenerTransport<GnssNavigationMessage.Callback> {
|
||||
|
||||
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<GnssMeasurementRequest, GnssMeasurementsEvent.Callback> {
|
||||
|
||||
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<GnssMeasurementRequest> 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<Void, GnssNavigationMessage.Callback> {
|
||||
|
||||
@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<Void, GnssAntennaInfo.Listener> {
|
||||
|
||||
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<GnssAntennaInfo> 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3334,12 +3378,6 @@ public class LocationManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
private static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY =
|
||||
"cache_key.location_enabled";
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -3351,8 +3389,6 @@ public class LocationManager {
|
||||
* @hide
|
||||
*/
|
||||
public void disableLocalLocationEnabledCaches() {
|
||||
synchronized (mLock) {
|
||||
mLocationEnabledCache = null;
|
||||
}
|
||||
mLocationEnabledCache = null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user