1/*
2 * Copyright (C) 2013 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 */
16package com.android.server.location;
17
18import android.content.ComponentName;
19import android.content.Intent;
20import android.content.ServiceConnection;
21import android.hardware.location.GeofenceHardwareService;
22import android.hardware.location.IGeofenceHardware;
23import android.location.IGeofenceProvider;
24import android.location.IGpsGeofenceHardware;
25import android.location.IFusedGeofenceHardware;
26import android.content.Context;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.Message;
30import android.os.RemoteException;
31import android.os.UserHandle;
32import android.util.Log;
33import com.android.server.ServiceWatcher;
34
35/**
36 * @hide
37 */
38public final class GeofenceProxy {
39    private static final String TAG = "GeofenceProxy";
40    private static final String SERVICE_ACTION =
41            "com.android.location.service.GeofenceProvider";
42    private final ServiceWatcher mServiceWatcher;
43    private final Context mContext;
44    private final IGpsGeofenceHardware mGpsGeofenceHardware;
45    private final IFusedGeofenceHardware mFusedGeofenceHardware;
46
47    private final Object mLock = new Object();
48
49    // Access to mGeofenceHardware needs to be synchronized by mLock.
50    private IGeofenceHardware mGeofenceHardware;
51
52    private static final int GEOFENCE_PROVIDER_CONNECTED = 1;
53    private static final int GEOFENCE_HARDWARE_CONNECTED = 2;
54    private static final int GEOFENCE_HARDWARE_DISCONNECTED = 3;
55    private static final int GEOFENCE_GPS_HARDWARE_CONNECTED = 4;
56    private static final int GEOFENCE_GPS_HARDWARE_DISCONNECTED = 5;
57
58    private Runnable mRunnable = new Runnable() {
59        @Override
60        public void run() {
61            mHandler.sendEmptyMessage(GEOFENCE_PROVIDER_CONNECTED);
62        }
63    };
64
65    public static GeofenceProxy createAndBind(Context context,
66            int overlaySwitchResId, int defaultServicePackageNameResId,
67            int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence,
68            IFusedGeofenceHardware fusedGeofenceHardware) {
69        GeofenceProxy proxy = new GeofenceProxy(context, overlaySwitchResId,
70            defaultServicePackageNameResId, initialPackageNamesResId, handler, gpsGeofence,
71            fusedGeofenceHardware);
72        if (proxy.bindGeofenceProvider()) {
73            return proxy;
74        } else {
75            return null;
76        }
77    }
78
79    private GeofenceProxy(Context context,
80            int overlaySwitchResId, int defaultServicePackageNameResId,
81            int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence,
82            IFusedGeofenceHardware fusedGeofenceHardware) {
83        mContext = context;
84        mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
85            defaultServicePackageNameResId, initialPackageNamesResId, mRunnable, handler);
86        mGpsGeofenceHardware = gpsGeofence;
87        mFusedGeofenceHardware = fusedGeofenceHardware;
88        bindHardwareGeofence();
89    }
90
91    private boolean bindGeofenceProvider() {
92        return mServiceWatcher.start();
93    }
94
95    private void bindHardwareGeofence() {
96        mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class),
97                mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.OWNER);
98    }
99
100    private ServiceConnection mServiceConnection = new ServiceConnection() {
101        @Override
102        public void onServiceConnected(ComponentName name, IBinder service) {
103            synchronized (mLock) {
104                mGeofenceHardware = IGeofenceHardware.Stub.asInterface(service);
105                mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_CONNECTED);
106            }
107        }
108
109        @Override
110        public void onServiceDisconnected(ComponentName name) {
111            synchronized (mLock) {
112                mGeofenceHardware = null;
113                mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_DISCONNECTED);
114            }
115        }
116    };
117
118    private void setGeofenceHardwareInProviderLocked() {
119        try {
120            IGeofenceProvider provider = IGeofenceProvider.Stub.asInterface(
121                      mServiceWatcher.getBinder());
122            if (provider != null) {
123                provider.setGeofenceHardware(mGeofenceHardware);
124            }
125        } catch (RemoteException e) {
126            Log.e(TAG, "Remote Exception: setGeofenceHardwareInProviderLocked: " + e);
127        }
128    }
129
130    private void setGpsGeofenceLocked() {
131        try {
132            mGeofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
133        } catch (RemoteException e) {
134            Log.e(TAG, "Error while connecting to GeofenceHardwareService");
135        }
136    }
137
138    private void setFusedGeofenceLocked() {
139        try {
140            mGeofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware);
141        } catch(RemoteException e) {
142            Log.e(TAG, "Error while connecting to GeofenceHardwareService");
143        }
144    }
145
146    // This needs to be reworked, when more services get added,
147    // Might need a state machine or add a framework utility class,
148    private Handler mHandler = new Handler() {
149
150        @Override
151        public void handleMessage(Message msg) {
152            switch (msg.what) {
153                case GEOFENCE_PROVIDER_CONNECTED:
154                    synchronized (mLock) {
155                        if (mGeofenceHardware != null) {
156                            setGeofenceHardwareInProviderLocked();
157                        }
158                        // else: the geofence provider will be notified when the connection to
159                        // GeofenceHardwareService is established.
160                    }
161                    break;
162                case GEOFENCE_HARDWARE_CONNECTED:
163                    synchronized (mLock) {
164                        // Theoretically this won't happen because once the GeofenceHardwareService
165                        // is connected to, we won't lose connection to it because it's a system
166                        // service. But this check does make the code more robust.
167                        if (mGeofenceHardware != null) {
168                            setGpsGeofenceLocked();
169                            setFusedGeofenceLocked();
170                            setGeofenceHardwareInProviderLocked();
171                        }
172                    }
173                    break;
174                case GEOFENCE_HARDWARE_DISCONNECTED:
175                    synchronized (mLock) {
176                        if (mGeofenceHardware == null) {
177                            setGeofenceHardwareInProviderLocked();
178                        }
179                    }
180                    break;
181            }
182        }
183    };
184}
185