1/*
2 * Copyright (C) 2009 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
17package com.android.server.location;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.location.Criteria;
24import android.location.ILocationProvider;
25import android.location.Location;
26import android.net.NetworkInfo;
27import android.os.Bundle;
28import android.os.Handler;
29import android.os.IBinder;
30import android.os.RemoteException;
31import android.os.WorkSource;
32import android.util.Log;
33
34import com.android.internal.location.DummyLocationProvider;
35
36/**
37 * A class for proxying location providers implemented as services.
38 *
39 * {@hide}
40 */
41public class LocationProviderProxy implements LocationProviderInterface {
42
43    private static final String TAG = "LocationProviderProxy";
44
45    private final Context mContext;
46    private final String mName;
47    private final Intent mIntent;
48    private final Handler mHandler;
49    private final Object mMutex = new Object();  // synchronizes access to non-final members
50    private Connection mServiceConnection = new Connection();  // never null
51
52    // cached values set by the location manager
53    private boolean mLocationTracking = false;
54    private boolean mEnabled = false;
55    private long mMinTime = -1;
56    private WorkSource mMinTimeSource = new WorkSource();
57    private int mNetworkState;
58    private NetworkInfo mNetworkInfo;
59
60    // constructor for proxying location providers implemented in a separate service
61    public LocationProviderProxy(Context context, String name, String serviceName,
62            Handler handler) {
63        mContext = context;
64        mName = name;
65        mIntent = new Intent(serviceName);
66        mHandler = handler;
67        mContext.bindService(mIntent, mServiceConnection,
68                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
69                | Context.BIND_ALLOW_OOM_MANAGEMENT);
70    }
71
72    /**
73     * When unbundled NetworkLocationService package is updated, we
74     * need to unbind from the old version and re-bind to the new one.
75     */
76    public void reconnect() {
77        synchronized (mMutex) {
78            mContext.unbindService(mServiceConnection);
79            mServiceConnection = new Connection();
80            mContext.bindService(mIntent, mServiceConnection,
81                    Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
82                    | Context.BIND_ALLOW_OOM_MANAGEMENT);
83        }
84    }
85
86    private class Connection implements ServiceConnection, Runnable {
87
88        private ILocationProvider mProvider;
89
90        // for caching requiresNetwork, requiresSatellite, etc.
91        private DummyLocationProvider mCachedAttributes;  // synchronized by mMutex
92
93        public void onServiceConnected(ComponentName className, IBinder service) {
94            synchronized (this) {
95                mProvider = ILocationProvider.Stub.asInterface(service);
96                if (mProvider != null) {
97                    mHandler.post(this);
98                }
99            }
100        }
101
102        public void onServiceDisconnected(ComponentName className) {
103            synchronized (this) {
104                mProvider = null;
105            }
106        }
107
108        public synchronized ILocationProvider getProvider() {
109            return mProvider;
110        }
111
112        public synchronized DummyLocationProvider getCachedAttributes() {
113            return mCachedAttributes;
114        }
115
116        public void run() {
117            synchronized (mMutex) {
118                if (mServiceConnection != this) {
119                    // This ServiceConnection no longer the one we want to bind to.
120                    return;
121                }
122                ILocationProvider provider = getProvider();
123                if (provider == null) {
124                    return;
125                }
126
127                // resend previous values from the location manager if the service has restarted
128                try {
129                    if (mEnabled) {
130                        provider.enable();
131                    }
132                    if (mLocationTracking) {
133                        provider.enableLocationTracking(true);
134                    }
135                    if (mMinTime >= 0) {
136                        provider.setMinTime(mMinTime, mMinTimeSource);
137                    }
138                    if (mNetworkInfo != null) {
139                        provider.updateNetworkState(mNetworkState, mNetworkInfo);
140                    }
141                } catch (RemoteException e) {
142                }
143
144                // init cache of parameters
145                if (mCachedAttributes == null) {
146                    try {
147                        mCachedAttributes = new DummyLocationProvider(mName, null);
148                        mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
149                        mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
150                        mCachedAttributes.setRequiresCell(provider.requiresCell());
151                        mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost());
152                        mCachedAttributes.setSupportsAltitude(provider.supportsAltitude());
153                        mCachedAttributes.setSupportsSpeed(provider.supportsSpeed());
154                        mCachedAttributes.setSupportsBearing(provider.supportsBearing());
155                        mCachedAttributes.setPowerRequirement(provider.getPowerRequirement());
156                        mCachedAttributes.setAccuracy(provider.getAccuracy());
157                    } catch (RemoteException e) {
158                        mCachedAttributes = null;
159                    }
160                }
161            }
162        }
163    };
164
165    public String getName() {
166        return mName;
167    }
168
169    private DummyLocationProvider getCachedAttributes() {
170        synchronized (mMutex) {
171            return mServiceConnection.getCachedAttributes();
172        }
173    }
174
175    public boolean requiresNetwork() {
176        DummyLocationProvider cachedAttributes = getCachedAttributes();
177        if (cachedAttributes != null) {
178            return cachedAttributes.requiresNetwork();
179        } else {
180            return false;
181        }
182    }
183
184    public boolean requiresSatellite() {
185        DummyLocationProvider cachedAttributes = getCachedAttributes();
186        if (cachedAttributes != null) {
187            return cachedAttributes.requiresSatellite();
188        } else {
189            return false;
190        }
191    }
192
193    public boolean requiresCell() {
194        DummyLocationProvider cachedAttributes = getCachedAttributes();
195        if (cachedAttributes != null) {
196            return cachedAttributes.requiresCell();
197        } else {
198            return false;
199        }
200    }
201
202    public boolean hasMonetaryCost() {
203        DummyLocationProvider cachedAttributes = getCachedAttributes();
204        if (cachedAttributes != null) {
205            return cachedAttributes.hasMonetaryCost();
206        } else {
207            return false;
208        }
209    }
210
211    public boolean supportsAltitude() {
212        DummyLocationProvider cachedAttributes = getCachedAttributes();
213        if (cachedAttributes != null) {
214            return cachedAttributes.supportsAltitude();
215        } else {
216            return false;
217        }
218    }
219
220    public boolean supportsSpeed() {
221        DummyLocationProvider cachedAttributes = getCachedAttributes();
222        if (cachedAttributes != null) {
223            return cachedAttributes.supportsSpeed();
224        } else {
225            return false;
226        }
227    }
228
229     public boolean supportsBearing() {
230        DummyLocationProvider cachedAttributes = getCachedAttributes();
231        if (cachedAttributes != null) {
232            return cachedAttributes.supportsBearing();
233        } else {
234            return false;
235        }
236    }
237
238    public int getPowerRequirement() {
239        DummyLocationProvider cachedAttributes = getCachedAttributes();
240        if (cachedAttributes != null) {
241            return cachedAttributes.getPowerRequirement();
242        } else {
243            return -1;
244        }
245    }
246
247    public int getAccuracy() {
248        DummyLocationProvider cachedAttributes = getCachedAttributes();
249        if (cachedAttributes != null) {
250            return cachedAttributes.getAccuracy();
251        } else {
252            return -1;
253        }
254    }
255
256    public boolean meetsCriteria(Criteria criteria) {
257        synchronized (mMutex) {
258            ILocationProvider provider = mServiceConnection.getProvider();
259            if (provider != null) {
260                try {
261                    return provider.meetsCriteria(criteria);
262                } catch (RemoteException e) {
263                }
264            }
265        }
266        // default implementation if we lost connection to the provider
267        if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
268            (criteria.getAccuracy() < getAccuracy())) {
269            return false;
270        }
271        int criteriaPower = criteria.getPowerRequirement();
272        if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
273            (criteriaPower < getPowerRequirement())) {
274            return false;
275        }
276        if (criteria.isAltitudeRequired() && !supportsAltitude()) {
277            return false;
278        }
279        if (criteria.isSpeedRequired() && !supportsSpeed()) {
280            return false;
281        }
282        if (criteria.isBearingRequired() && !supportsBearing()) {
283            return false;
284        }
285        return true;
286    }
287
288    public void enable() {
289        synchronized (mMutex) {
290            mEnabled = true;
291            ILocationProvider provider = mServiceConnection.getProvider();
292            if (provider != null) {
293                try {
294                    provider.enable();
295                } catch (RemoteException e) {
296                }
297            }
298        }
299    }
300
301    public void disable() {
302        synchronized (mMutex) {
303            mEnabled = false;
304            ILocationProvider provider = mServiceConnection.getProvider();
305            if (provider != null) {
306                try {
307                    provider.disable();
308                } catch (RemoteException e) {
309                }
310            }
311        }
312    }
313
314    public boolean isEnabled() {
315        synchronized (mMutex) {
316            return mEnabled;
317        }
318    }
319
320    public int getStatus(Bundle extras) {
321        ILocationProvider provider;
322        synchronized (mMutex) {
323            provider = mServiceConnection.getProvider();
324        }
325        if (provider != null) {
326            try {
327                return provider.getStatus(extras);
328            } catch (RemoteException e) {
329            }
330        }
331        return 0;
332    }
333
334    public long getStatusUpdateTime() {
335        ILocationProvider provider;
336        synchronized (mMutex) {
337            provider = mServiceConnection.getProvider();
338        }
339        if (provider != null) {
340            try {
341                return provider.getStatusUpdateTime();
342            } catch (RemoteException e) {
343            }
344        }
345        return 0;
346     }
347
348    public String getInternalState() {
349        ILocationProvider provider;
350        synchronized (mMutex) {
351            provider = mServiceConnection.getProvider();
352        }
353        if (provider != null) {
354            try {
355                return provider.getInternalState();
356            } catch (RemoteException e) {
357                Log.e(TAG, "getInternalState failed", e);
358            }
359        }
360        return null;
361    }
362
363    public boolean isLocationTracking() {
364        synchronized (mMutex) {
365            return mLocationTracking;
366        }
367    }
368
369    public void enableLocationTracking(boolean enable) {
370        synchronized (mMutex) {
371            mLocationTracking = enable;
372            if (!enable) {
373                mMinTime = -1;
374                mMinTimeSource.clear();
375            }
376            ILocationProvider provider = mServiceConnection.getProvider();
377            if (provider != null) {
378                try {
379                    provider.enableLocationTracking(enable);
380                } catch (RemoteException e) {
381                }
382            }
383        }
384    }
385
386    public boolean requestSingleShotFix() {
387        return false;
388    }
389
390    public long getMinTime() {
391        synchronized (mMutex) {
392            return mMinTime;
393        }
394    }
395
396    public void setMinTime(long minTime, WorkSource ws) {
397        synchronized (mMutex) {
398            mMinTime = minTime;
399            mMinTimeSource.set(ws);
400            ILocationProvider provider = mServiceConnection.getProvider();
401            if (provider != null) {
402                try {
403                    provider.setMinTime(minTime, ws);
404                } catch (RemoteException e) {
405                }
406            }
407        }
408    }
409
410    public void updateNetworkState(int state, NetworkInfo info) {
411        synchronized (mMutex) {
412            mNetworkState = state;
413            mNetworkInfo = info;
414            ILocationProvider provider = mServiceConnection.getProvider();
415            if (provider != null) {
416                try {
417                    provider.updateNetworkState(state, info);
418                } catch (RemoteException e) {
419                }
420            }
421        }
422    }
423
424    public void updateLocation(Location location) {
425        synchronized (mMutex) {
426            ILocationProvider provider = mServiceConnection.getProvider();
427            if (provider != null) {
428                try {
429                    provider.updateLocation(location);
430                } catch (RemoteException e) {
431                }
432            }
433        }
434    }
435
436    public boolean sendExtraCommand(String command, Bundle extras) {
437        synchronized (mMutex) {
438            ILocationProvider provider = mServiceConnection.getProvider();
439            if (provider != null) {
440                try {
441                    return provider.sendExtraCommand(command, extras);
442                } catch (RemoteException e) {
443                }
444            }
445        }
446        return false;
447    }
448
449    public void addListener(int uid) {
450        synchronized (mMutex) {
451            ILocationProvider provider = mServiceConnection.getProvider();
452            if (provider != null) {
453                try {
454                    provider.addListener(uid);
455                } catch (RemoteException e) {
456                }
457            }
458        }
459    }
460
461    public void removeListener(int uid) {
462        synchronized (mMutex) {
463            ILocationProvider provider = mServiceConnection.getProvider();
464            if (provider != null) {
465                try {
466                    provider.removeListener(uid);
467                } catch (RemoteException e) {
468                }
469            }
470        }
471    }
472}
473