1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.location; 18 19 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; 20 21 import android.annotation.Nullable; 22 import android.location.Location; 23 import android.os.Bundle; 24 25 import com.android.internal.annotations.GuardedBy; 26 import com.android.internal.location.ProviderRequest; 27 import com.android.internal.util.Preconditions; 28 29 import java.io.FileDescriptor; 30 import java.io.PrintWriter; 31 import java.util.Collections; 32 import java.util.List; 33 34 /** 35 * Represents a location provider that may switch between a mock implementation and a real 36 * implementation. Requires owners to provide a lock object that will be used internally and held 37 * for the duration of all listener callbacks. Owners are reponsible for ensuring this cannot lead 38 * to deadlock. 39 * 40 * In order to ensure deadlock does not occur, the owner must validate that the ONLY lock which can 41 * be held BOTH when calling into this class AND when receiving a callback from this class is the 42 * lock given to this class via the constructor. Holding any other lock is ok as long as there is no 43 * possibility that it can be obtained within both codepaths. 44 * 45 * Holding the given lock guarantees atomicity of any operations on this class for the duration. 46 * 47 * @hide 48 */ 49 public class MockableLocationProvider extends AbstractLocationProvider { 50 51 private final Object mOwnerLock; 52 53 @GuardedBy("mOwnerLock") 54 @Nullable private AbstractLocationProvider mProvider; 55 @GuardedBy("mOwnerLock") 56 @Nullable private AbstractLocationProvider mRealProvider; 57 @GuardedBy("mOwnerLock") 58 @Nullable private MockProvider mMockProvider; 59 60 @GuardedBy("mOwnerLock") 61 private ProviderRequest mRequest; 62 63 /** 64 * The given lock object will be held any time the listener is invoked, and may also be acquired 65 * and released during the course of invoking any public methods. Holding the given lock ensures 66 * that provider state cannot change except as result of an explicit call by the owner of the 67 * lock into this class. The client is reponsible for ensuring this cannot cause deadlock. 68 * 69 * The client should expect that it may being to receive callbacks as soon as this constructor 70 * is invoked. 71 */ MockableLocationProvider(Object ownerLock, Listener listener)72 public MockableLocationProvider(Object ownerLock, Listener listener) { 73 // using a direct executor is acceptable because all inbound calls are delegated to the 74 // actual provider implementations which will use their own executors 75 super(DIRECT_EXECUTOR, Collections.emptySet()); 76 mOwnerLock = ownerLock; 77 mRequest = ProviderRequest.EMPTY_REQUEST; 78 79 setListener(listener); 80 } 81 82 /** 83 * Returns the current provider implementation. May be null if there is no current 84 * implementation. 85 */ 86 @Nullable getProvider()87 public AbstractLocationProvider getProvider() { 88 synchronized (mOwnerLock) { 89 return mProvider; 90 } 91 } 92 93 /** 94 * Sets the real provider implementation, replacing any previous real provider implementation. 95 * May cause an inline invocation of {@link Listener#onStateChanged(State, State)} if this 96 * results in a state change. 97 */ setRealProvider(@ullable AbstractLocationProvider provider)98 public void setRealProvider(@Nullable AbstractLocationProvider provider) { 99 synchronized (mOwnerLock) { 100 if (mRealProvider == provider) { 101 return; 102 } 103 104 mRealProvider = provider; 105 if (!isMock()) { 106 setProviderLocked(mRealProvider); 107 } 108 } 109 } 110 111 /** 112 * Sets the mock provider implementation, replacing any previous mock provider implementation. 113 * Mock implementations are always used instead of real implementations if set. May cause an 114 * inline invocation of {@link Listener#onStateChanged(State, State)} if this results in a 115 * state change. 116 */ setMockProvider(@ullable MockProvider provider)117 public void setMockProvider(@Nullable MockProvider provider) { 118 synchronized (mOwnerLock) { 119 if (mMockProvider == provider) { 120 return; 121 } 122 123 mMockProvider = provider; 124 if (mMockProvider != null) { 125 setProviderLocked(mMockProvider); 126 } else { 127 setProviderLocked(mRealProvider); 128 } 129 } 130 } 131 132 @GuardedBy("mOwnerLock") setProviderLocked(@ullable AbstractLocationProvider provider)133 private void setProviderLocked(@Nullable AbstractLocationProvider provider) { 134 if (mProvider == provider) { 135 return; 136 } 137 138 AbstractLocationProvider oldProvider = mProvider; 139 mProvider = provider; 140 141 if (oldProvider != null) { 142 // do this after switching the provider - so even if the old provider is using a direct 143 // executor, if it re-enters this class within setRequest(), it will be ignored 144 oldProvider.setListener(null); 145 oldProvider.setRequest(ProviderRequest.EMPTY_REQUEST); 146 } 147 148 State newState; 149 if (mProvider != null) { 150 newState = mProvider.setListener(new ListenerWrapper(mProvider)); 151 } else { 152 newState = State.EMPTY_STATE; 153 } 154 155 ProviderRequest oldRequest = mRequest; 156 setState(newState); 157 158 if (mProvider != null && oldRequest == mRequest) { 159 mProvider.setRequest(mRequest); 160 } 161 } 162 163 /** 164 * Returns true if the current active provider implementation is the mock implementation, and 165 * false otherwise. 166 */ isMock()167 public boolean isMock() { 168 synchronized (mOwnerLock) { 169 return mMockProvider != null && mProvider == mMockProvider; 170 } 171 } 172 173 /** 174 * Sets the mock provider implementation's allowed state. Will throw an exception if the mock 175 * provider is not currently the active implementation. 176 */ setMockProviderAllowed(boolean allowed)177 public void setMockProviderAllowed(boolean allowed) { 178 synchronized (mOwnerLock) { 179 Preconditions.checkState(isMock()); 180 mMockProvider.setProviderAllowed(allowed); 181 } 182 } 183 /** 184 * Sets the mock provider implementation's location. Will throw an exception if the mock 185 * provider is not currently the active implementation. 186 */ setMockProviderLocation(Location location)187 public void setMockProviderLocation(Location location) { 188 synchronized (mOwnerLock) { 189 Preconditions.checkState(isMock()); 190 mMockProvider.setProviderLocation(location); 191 } 192 } 193 194 /** 195 * Returns the current location request. 196 */ getCurrentRequest()197 public ProviderRequest getCurrentRequest() { 198 synchronized (mOwnerLock) { 199 return mRequest; 200 } 201 } 202 203 @Override onSetRequest(ProviderRequest request)204 protected void onSetRequest(ProviderRequest request) { 205 synchronized (mOwnerLock) { 206 if (request == mRequest) { 207 return; 208 } 209 210 mRequest = request; 211 212 if (mProvider != null) { 213 mProvider.setRequest(request); 214 } 215 } 216 } 217 218 @Override onExtraCommand(int uid, int pid, String command, Bundle extras)219 protected void onExtraCommand(int uid, int pid, String command, Bundle extras) { 220 synchronized (mOwnerLock) { 221 if (mProvider != null) { 222 mProvider.sendExtraCommand(uid, pid, command, extras); 223 } 224 } 225 } 226 227 /** 228 * Dumps the current provider implementation. 229 */ dump(FileDescriptor fd, PrintWriter pw, String[] args)230 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 231 // holding the owner lock outside this could lead to deadlock since we don't run dump on the 232 // executor specified by the provider, we run it directly 233 Preconditions.checkState(!Thread.holdsLock(mOwnerLock)); 234 235 AbstractLocationProvider provider; 236 synchronized (mOwnerLock) { 237 provider = mProvider; 238 pw.println("allowed=" + getState().allowed); 239 pw.println("properties=" + getState().properties); 240 pw.println("packages=" + getState().providerPackageNames); 241 pw.println("request=" + mRequest); 242 } 243 244 if (provider != null) { 245 // dump outside the lock in case the provider wants to acquire its own locks, and since 246 // the default provider dump behavior does not move things onto the provider thread... 247 provider.dump(fd, pw, args); 248 } 249 } 250 251 // ensures that callbacks from the incorrect provider are never visible to clients - this 252 // requires holding the owner's lock for the duration of the callback 253 private class ListenerWrapper implements Listener { 254 255 private final AbstractLocationProvider mListenerProvider; 256 ListenerWrapper(AbstractLocationProvider listenerProvider)257 private ListenerWrapper(AbstractLocationProvider listenerProvider) { 258 mListenerProvider = listenerProvider; 259 } 260 261 @Override onStateChanged(State oldState, State newState)262 public final void onStateChanged(State oldState, State newState) { 263 synchronized (mOwnerLock) { 264 if (mListenerProvider != mProvider) { 265 return; 266 } 267 268 setState(newState); 269 } 270 } 271 272 @Override onReportLocation(Location location)273 public final void onReportLocation(Location location) { 274 synchronized (mOwnerLock) { 275 if (mListenerProvider != mProvider) { 276 return; 277 } 278 279 reportLocation(location); 280 } 281 } 282 283 @Override onReportLocation(List<Location> locations)284 public final void onReportLocation(List<Location> locations) { 285 synchronized (mOwnerLock) { 286 if (mListenerProvider != mProvider) { 287 return; 288 } 289 290 reportLocation(locations); 291 } 292 } 293 } 294 } 295