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 java.io.FileDescriptor;
20import java.io.PrintWriter;
21
22import android.content.Context;
23import android.location.LocationProvider;
24import android.os.Bundle;
25import android.os.Handler;
26import android.os.RemoteException;
27import android.os.WorkSource;
28import android.util.Log;
29
30import com.android.internal.location.ProviderProperties;
31import com.android.internal.location.ILocationProvider;
32import com.android.internal.location.ProviderRequest;
33import com.android.server.LocationManagerService;
34import com.android.server.ServiceWatcher;
35
36/**
37 * Proxy for ILocationProvider implementations.
38 */
39public class LocationProviderProxy implements LocationProviderInterface {
40    private static final String TAG = "LocationProviderProxy";
41    private static final boolean D = LocationManagerService.D;
42
43    private final Context mContext;
44    private final String mName;
45    private final ServiceWatcher mServiceWatcher;
46
47    private Object mLock = new Object();
48
49    // cached values set by the location manager, synchronized on mLock
50    private ProviderProperties mProperties;
51    private boolean mEnabled = false;
52    private ProviderRequest mRequest = null;
53    private WorkSource mWorksource = new WorkSource();
54
55    public static LocationProviderProxy createAndBind(
56            Context context, String name, String action,
57            int overlaySwitchResId, int defaultServicePackageNameResId,
58            int initialPackageNamesResId, Handler handler) {
59        LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
60                overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId,
61                handler);
62        if (proxy.bind()) {
63            return proxy;
64        } else {
65            return null;
66        }
67    }
68
69    private LocationProviderProxy(Context context, String name, String action,
70            int overlaySwitchResId, int defaultServicePackageNameResId,
71            int initialPackageNamesResId, Handler handler) {
72        mContext = context;
73        mName = name;
74        mServiceWatcher = new ServiceWatcher(mContext, TAG + "-" + name, action, overlaySwitchResId,
75                defaultServicePackageNameResId, initialPackageNamesResId,
76                mNewServiceWork, handler);
77    }
78
79    private boolean bind () {
80        return mServiceWatcher.start();
81    }
82
83    private ILocationProvider getService() {
84        return ILocationProvider.Stub.asInterface(mServiceWatcher.getBinder());
85    }
86
87    public String getConnectedPackageName() {
88        return mServiceWatcher.getBestPackageName();
89    }
90
91    /**
92     * Work to apply current state to a newly connected provider.
93     * Remember we can switch the service that implements a providers
94     * at run-time, so need to apply current state.
95     */
96    private Runnable mNewServiceWork = new Runnable() {
97        @Override
98        public void run() {
99            if (D) Log.d(TAG, "applying state to connected service");
100
101            boolean enabled;
102            ProviderProperties properties = null;
103            ProviderRequest request;
104            WorkSource source;
105            ILocationProvider service;
106            synchronized (mLock) {
107                enabled = mEnabled;
108                request = mRequest;
109                source = mWorksource;
110                service = getService();
111            }
112
113            if (service == null) return;
114
115            try {
116                // load properties from provider
117                properties = service.getProperties();
118                if (properties == null) {
119                    Log.e(TAG, mServiceWatcher.getBestPackageName() +
120                            " has invalid locatino provider properties");
121                }
122
123                // apply current state to new service
124                if (enabled) {
125                    service.enable();
126                    if (request != null) {
127                        service.setRequest(request, source);
128                    }
129                }
130            } catch (RemoteException e) {
131                Log.w(TAG, e);
132            } catch (Exception e) {
133                // never let remote service crash system server
134                Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
135            }
136
137            synchronized (mLock) {
138                mProperties = properties;
139            }
140        }
141    };
142
143    @Override
144    public String getName() {
145        return mName;
146    }
147
148    @Override
149    public ProviderProperties getProperties() {
150        synchronized (mLock) {
151            return mProperties;
152        }
153    }
154
155    @Override
156    public void enable() {
157        synchronized (mLock) {
158            mEnabled = true;
159        }
160        ILocationProvider service = getService();
161        if (service == null) return;
162
163        try {
164            service.enable();
165        } catch (RemoteException e) {
166            Log.w(TAG, e);
167        } catch (Exception e) {
168            // never let remote service crash system server
169            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
170        }
171    }
172
173    @Override
174    public void disable() {
175        synchronized (mLock) {
176            mEnabled = false;
177        }
178        ILocationProvider service = getService();
179        if (service == null) return;
180
181        try {
182            service.disable();
183        } catch (RemoteException e) {
184            Log.w(TAG, e);
185        } catch (Exception e) {
186            // never let remote service crash system server
187            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
188        }
189    }
190
191    @Override
192    public boolean isEnabled() {
193        synchronized (mLock) {
194            return mEnabled;
195        }
196    }
197
198    @Override
199    public void setRequest(ProviderRequest request, WorkSource source) {
200        synchronized (mLock) {
201            mRequest = request;
202            mWorksource = source;
203        }
204        ILocationProvider service = getService();
205        if (service == null) return;
206
207        try {
208            service.setRequest(request, source);
209        } catch (RemoteException e) {
210            Log.w(TAG, e);
211        } catch (Exception e) {
212            // never let remote service crash system server
213            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
214        }
215    }
216
217    @Override
218    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
219        pw.append("REMOTE SERVICE");
220        pw.append(" name=").append(mName);
221        pw.append(" pkg=").append(mServiceWatcher.getBestPackageName());
222        pw.append(" version=").append("" + mServiceWatcher.getBestVersion());
223        pw.append('\n');
224
225        ILocationProvider service = getService();
226        if (service == null) {
227            pw.println("service down (null)");
228            return;
229        }
230        pw.flush();
231
232        try {
233            service.asBinder().dump(fd, args);
234        } catch (RemoteException e) {
235            pw.println("service down (RemoteException)");
236            Log.w(TAG, e);
237        } catch (Exception e) {
238            pw.println("service down (Exception)");
239            // never let remote service crash system server
240            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
241        }
242    }
243
244    @Override
245    public int getStatus(Bundle extras) {
246        ILocationProvider service = getService();
247        if (service == null) return LocationProvider.TEMPORARILY_UNAVAILABLE;
248
249        try {
250            return service.getStatus(extras);
251        } catch (RemoteException e) {
252            Log.w(TAG, e);
253        } catch (Exception e) {
254            // never let remote service crash system server
255            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
256        }
257        return LocationProvider.TEMPORARILY_UNAVAILABLE;
258    }
259
260    @Override
261    public long getStatusUpdateTime() {
262        ILocationProvider service = getService();
263        if (service == null) return 0;
264
265        try {
266            return service.getStatusUpdateTime();
267        } catch (RemoteException e) {
268            Log.w(TAG, e);
269        } catch (Exception e) {
270            // never let remote service crash system server
271            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
272        }
273        return 0;
274    }
275
276    @Override
277    public boolean sendExtraCommand(String command, Bundle extras) {
278        ILocationProvider service = getService();
279        if (service == null) return false;
280
281        try {
282            return service.sendExtraCommand(command, extras);
283        } catch (RemoteException e) {
284            Log.w(TAG, e);
285        } catch (Exception e) {
286            // never let remote service crash system server
287            Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
288        }
289        return false;
290    }
291 }
292