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 */ 16 17 package com.android.location.provider; 18 19 import android.hardware.location.IFusedLocationHardware; 20 import android.hardware.location.IFusedLocationHardwareSink; 21 22 import android.location.Location; 23 24 import android.os.Handler; 25 import android.os.Looper; 26 import android.os.Message; 27 import android.os.RemoteException; 28 import android.util.Log; 29 30 import java.util.HashMap; 31 import java.util.Map; 32 33 /** 34 * Class that exposes IFusedLocationHardware functionality to unbundled services. 35 */ 36 public final class FusedLocationHardware { 37 private final String TAG = "FusedLocationHardware"; 38 39 private IFusedLocationHardware mLocationHardware; 40 41 // the list uses a copy-on-write pattern to update its contents 42 HashMap<FusedLocationHardwareSink, DispatcherHandler> mSinkList = 43 new HashMap<FusedLocationHardwareSink, DispatcherHandler>(); 44 45 private IFusedLocationHardwareSink mInternalSink = new IFusedLocationHardwareSink.Stub() { 46 @Override 47 public void onLocationAvailable(Location[] locations) { 48 dispatchLocations(locations); 49 } 50 51 @Override 52 public void onDiagnosticDataAvailable(String data) { 53 dispatchDiagnosticData(data); 54 } 55 }; 56 57 /** 58 * @hide 59 */ 60 public FusedLocationHardware(IFusedLocationHardware locationHardware) { 61 mLocationHardware = locationHardware; 62 } 63 64 /* 65 * Methods to provide a Facade for IFusedLocationHardware 66 */ 67 public void registerSink(FusedLocationHardwareSink sink, Looper looper) { 68 if(sink == null || looper == null) { 69 throw new IllegalArgumentException("Parameter sink and looper cannot be null."); 70 } 71 72 boolean registerSink; 73 synchronized (mSinkList) { 74 // register only on first insertion 75 registerSink = mSinkList.size() == 0; 76 // guarantee uniqueness 77 if(mSinkList.containsKey(sink)) { 78 return; 79 } 80 81 HashMap<FusedLocationHardwareSink, DispatcherHandler> newSinkList = 82 new HashMap<FusedLocationHardwareSink, DispatcherHandler>(mSinkList); 83 newSinkList.put(sink, new DispatcherHandler(looper)); 84 mSinkList = newSinkList; 85 } 86 87 if(registerSink) { 88 try { 89 mLocationHardware.registerSink(mInternalSink); 90 } catch(RemoteException e) { 91 Log.e(TAG, "RemoteException at registerSink"); 92 } 93 } 94 } 95 96 public void unregisterSink(FusedLocationHardwareSink sink) { 97 if(sink == null) { 98 throw new IllegalArgumentException("Parameter sink cannot be null."); 99 } 100 101 boolean unregisterSink; 102 synchronized(mSinkList) { 103 if(!mSinkList.containsKey(sink)) { 104 //done 105 return; 106 } 107 108 HashMap<FusedLocationHardwareSink, DispatcherHandler> newSinkList = 109 new HashMap<FusedLocationHardwareSink, DispatcherHandler>(mSinkList); 110 newSinkList.remove(sink); 111 //unregister after the last sink 112 unregisterSink = newSinkList.size() == 0; 113 114 mSinkList = newSinkList; 115 } 116 117 if(unregisterSink) { 118 try { 119 mLocationHardware.unregisterSink(mInternalSink); 120 } catch(RemoteException e) { 121 Log.e(TAG, "RemoteException at unregisterSink"); 122 } 123 } 124 } 125 126 public int getSupportedBatchSize() { 127 try { 128 return mLocationHardware.getSupportedBatchSize(); 129 } catch(RemoteException e) { 130 Log.e(TAG, "RemoteException at getSupportedBatchSize"); 131 return 0; 132 } 133 } 134 135 public void startBatching(int id, GmsFusedBatchOptions batchOptions) { 136 try { 137 mLocationHardware.startBatching(id, batchOptions.getParcelableOptions()); 138 } catch(RemoteException e) { 139 Log.e(TAG, "RemoteException at startBatching"); 140 } 141 } 142 143 public void stopBatching(int id) { 144 try { 145 mLocationHardware.stopBatching(id); 146 } catch(RemoteException e) { 147 Log.e(TAG, "RemoteException at stopBatching"); 148 } 149 } 150 151 public void updateBatchingOptions(int id, GmsFusedBatchOptions batchOptions) { 152 try { 153 mLocationHardware.updateBatchingOptions(id, batchOptions.getParcelableOptions()); 154 } catch(RemoteException e) { 155 Log.e(TAG, "RemoteException at updateBatchingOptions"); 156 } 157 } 158 159 public void requestBatchOfLocations(int batchSizeRequest) { 160 try { 161 mLocationHardware.requestBatchOfLocations(batchSizeRequest); 162 } catch(RemoteException e) { 163 Log.e(TAG, "RemoteException at requestBatchOfLocations"); 164 } 165 } 166 167 public boolean supportsDiagnosticDataInjection() { 168 try { 169 return mLocationHardware.supportsDiagnosticDataInjection(); 170 } catch(RemoteException e) { 171 Log.e(TAG, "RemoteException at supportsDiagnisticDataInjection"); 172 return false; 173 } 174 } 175 176 public void injectDiagnosticData(String data) { 177 try { 178 mLocationHardware.injectDiagnosticData(data); 179 } catch(RemoteException e) { 180 Log.e(TAG, "RemoteException at injectDiagnosticData"); 181 } 182 } 183 184 public boolean supportsDeviceContextInjection() { 185 try { 186 return mLocationHardware.supportsDeviceContextInjection(); 187 } catch(RemoteException e) { 188 Log.e(TAG, "RemoteException at supportsDeviceContextInjection"); 189 return false; 190 } 191 } 192 193 public void injectDeviceContext(int deviceEnabledContext) { 194 try { 195 mLocationHardware.injectDeviceContext(deviceEnabledContext); 196 } catch(RemoteException e) { 197 Log.e(TAG, "RemoteException at injectDeviceContext"); 198 } 199 } 200 201 /* 202 * Helper methods and classes 203 */ 204 private class DispatcherHandler extends Handler { 205 public static final int DISPATCH_LOCATION = 1; 206 public static final int DISPATCH_DIAGNOSTIC_DATA = 2; 207 208 public DispatcherHandler(Looper looper) { 209 super(looper, null /*callback*/ , true /*async*/); 210 } 211 212 @Override 213 public void handleMessage(Message message) { 214 MessageCommand command = (MessageCommand) message.obj; 215 switch(message.what) { 216 case DISPATCH_LOCATION: 217 command.dispatchLocation(); 218 break; 219 case DISPATCH_DIAGNOSTIC_DATA: 220 command.dispatchDiagnosticData(); 221 default: 222 Log.e(TAG, "Invalid dispatch message"); 223 break; 224 } 225 } 226 } 227 228 private class MessageCommand { 229 private final FusedLocationHardwareSink mSink; 230 private final Location[] mLocations; 231 private final String mData; 232 233 public MessageCommand( 234 FusedLocationHardwareSink sink, 235 Location[] locations, 236 String data) { 237 mSink = sink; 238 mLocations = locations; 239 mData = data; 240 } 241 242 public void dispatchLocation() { 243 mSink.onLocationAvailable(mLocations); 244 } 245 246 public void dispatchDiagnosticData() { 247 mSink.onDiagnosticDataAvailable(mData); 248 } 249 } 250 251 private void dispatchLocations(Location[] locations) { 252 HashMap<FusedLocationHardwareSink, DispatcherHandler> sinks; 253 synchronized (mSinkList) { 254 sinks = mSinkList; 255 } 256 257 for(Map.Entry<FusedLocationHardwareSink, DispatcherHandler> entry : sinks.entrySet()) { 258 Message message = Message.obtain( 259 entry.getValue(), 260 DispatcherHandler.DISPATCH_LOCATION, 261 new MessageCommand(entry.getKey(), locations, null /*data*/)); 262 message.sendToTarget(); 263 } 264 } 265 266 private void dispatchDiagnosticData(String data) { 267 HashMap<FusedLocationHardwareSink, DispatcherHandler> sinks; 268 synchronized(mSinkList) { 269 sinks = mSinkList; 270 } 271 272 for(Map.Entry<FusedLocationHardwareSink, DispatcherHandler> entry : sinks.entrySet()) { 273 Message message = Message.obtain( 274 entry.getValue(), 275 DispatcherHandler.DISPATCH_DIAGNOSTIC_DATA, 276 new MessageCommand(entry.getKey(), null /*locations*/, data)); 277 message.sendToTarget(); 278 } 279 } 280 } 281