/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.location.provider; import android.hardware.location.IFusedLocationHardware; import android.hardware.location.IFusedLocationHardwareSink; import android.location.Location; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Log; import java.util.HashMap; import java.util.Map; /** * Class that exposes IFusedLocationHardware functionality to unbundled services. */ public final class FusedLocationHardware { private static final String TAG = "FusedLocationHardware"; private IFusedLocationHardware mLocationHardware; // the list uses a copy-on-write pattern to update its contents HashMap mSinkList = new HashMap(); private IFusedLocationHardwareSink mInternalSink = new IFusedLocationHardwareSink.Stub() { @Override public void onLocationAvailable(Location[] locations) { dispatchLocations(locations); } @Override public void onDiagnosticDataAvailable(String data) { dispatchDiagnosticData(data); } @Override public void onCapabilities(int capabilities) { dispatchCapabilities(capabilities); } @Override public void onStatusChanged(int status) { dispatchStatus(status); } }; /** * @hide */ public FusedLocationHardware(IFusedLocationHardware locationHardware) { mLocationHardware = locationHardware; } /* * Methods to provide a Facade for IFusedLocationHardware */ public void registerSink(FusedLocationHardwareSink sink, Looper looper) { if(sink == null || looper == null) { throw new IllegalArgumentException("Parameter sink and looper cannot be null."); } boolean registerSink; synchronized (mSinkList) { // register only on first insertion registerSink = mSinkList.size() == 0; // guarantee uniqueness if(mSinkList.containsKey(sink)) { return; } HashMap newSinkList = new HashMap(mSinkList); newSinkList.put(sink, new DispatcherHandler(looper)); mSinkList = newSinkList; } if(registerSink) { try { mLocationHardware.registerSink(mInternalSink); } catch(RemoteException e) { Log.e(TAG, "RemoteException at registerSink"); } } } public void unregisterSink(FusedLocationHardwareSink sink) { if(sink == null) { throw new IllegalArgumentException("Parameter sink cannot be null."); } boolean unregisterSink; synchronized(mSinkList) { if(!mSinkList.containsKey(sink)) { //done return; } HashMap newSinkList = new HashMap(mSinkList); newSinkList.remove(sink); //unregister after the last sink unregisterSink = newSinkList.size() == 0; mSinkList = newSinkList; } if(unregisterSink) { try { mLocationHardware.unregisterSink(mInternalSink); } catch(RemoteException e) { Log.e(TAG, "RemoteException at unregisterSink"); } } } public int getSupportedBatchSize() { try { return mLocationHardware.getSupportedBatchSize(); } catch(RemoteException e) { Log.e(TAG, "RemoteException at getSupportedBatchSize"); return 0; } } public void startBatching(int id, GmsFusedBatchOptions batchOptions) { try { mLocationHardware.startBatching(id, batchOptions.getParcelableOptions()); } catch(RemoteException e) { Log.e(TAG, "RemoteException at startBatching"); } } public void stopBatching(int id) { try { mLocationHardware.stopBatching(id); } catch(RemoteException e) { Log.e(TAG, "RemoteException at stopBatching"); } } public void updateBatchingOptions(int id, GmsFusedBatchOptions batchOptions) { try { mLocationHardware.updateBatchingOptions(id, batchOptions.getParcelableOptions()); } catch(RemoteException e) { Log.e(TAG, "RemoteException at updateBatchingOptions"); } } public void requestBatchOfLocations(int batchSizeRequest) { try { mLocationHardware.requestBatchOfLocations(batchSizeRequest); } catch(RemoteException e) { Log.e(TAG, "RemoteException at requestBatchOfLocations"); } } public void flushBatchedLocations() { try { mLocationHardware.flushBatchedLocations(); } catch(RemoteException e) { Log.e(TAG, "RemoteException at flushBatchedLocations"); } } public boolean supportsDiagnosticDataInjection() { try { return mLocationHardware.supportsDiagnosticDataInjection(); } catch(RemoteException e) { Log.e(TAG, "RemoteException at supportsDiagnisticDataInjection"); return false; } } public void injectDiagnosticData(String data) { try { mLocationHardware.injectDiagnosticData(data); } catch(RemoteException e) { Log.e(TAG, "RemoteException at injectDiagnosticData"); } } public boolean supportsDeviceContextInjection() { try { return mLocationHardware.supportsDeviceContextInjection(); } catch(RemoteException e) { Log.e(TAG, "RemoteException at supportsDeviceContextInjection"); return false; } } public void injectDeviceContext(int deviceEnabledContext) { try { mLocationHardware.injectDeviceContext(deviceEnabledContext); } catch(RemoteException e) { Log.e(TAG, "RemoteException at injectDeviceContext"); } } /** * Returns the version of the FLP HAL. * *

Version 1 is the initial release. *

Version 2 adds the ability to use {@link #flushBatchedLocations}, * {@link FusedLocationHardwareSink#onCapabilities}, and * {@link FusedLocationHardwareSink#onStatusChanged}. * *

This method is only available on API 23 or later. Older APIs have version 1. */ public int getVersion() { try { return mLocationHardware.getVersion(); } catch(RemoteException e) { Log.e(TAG, "RemoteException at getVersion"); } return 1; } /* * Helper methods and classes */ private class DispatcherHandler extends Handler { public static final int DISPATCH_LOCATION = 1; public static final int DISPATCH_DIAGNOSTIC_DATA = 2; public static final int DISPATCH_CAPABILITIES = 3; public static final int DISPATCH_STATUS = 4; public DispatcherHandler(Looper looper) { super(looper, null /*callback*/ , true /*async*/); } @Override public void handleMessage(Message message) { MessageCommand command = (MessageCommand) message.obj; switch(message.what) { case DISPATCH_LOCATION: command.dispatchLocation(); break; case DISPATCH_DIAGNOSTIC_DATA: command.dispatchDiagnosticData(); break; case DISPATCH_CAPABILITIES: command.dispatchCapabilities(); break; case DISPATCH_STATUS: command.dispatchStatus(); break; default: Log.e(TAG, "Invalid dispatch message"); break; } } } private class MessageCommand { private final FusedLocationHardwareSink mSink; private final Location[] mLocations; private final String mData; private final int mCapabilities; private final int mStatus; public MessageCommand( FusedLocationHardwareSink sink, Location[] locations, String data, int capabilities, int status) { mSink = sink; mLocations = locations; mData = data; mCapabilities = capabilities; mStatus = status; } public void dispatchLocation() { mSink.onLocationAvailable(mLocations); } public void dispatchDiagnosticData() { mSink.onDiagnosticDataAvailable(mData); } public void dispatchCapabilities() { mSink.onCapabilities(mCapabilities); } public void dispatchStatus() { mSink.onStatusChanged(mStatus); } } private void dispatchLocations(Location[] locations) { HashMap sinks; synchronized (mSinkList) { sinks = mSinkList; } for(Map.Entry entry : sinks.entrySet()) { Message message = Message.obtain( entry.getValue(), DispatcherHandler.DISPATCH_LOCATION, new MessageCommand(entry.getKey(), locations, null /*data*/, 0, 0)); message.sendToTarget(); } } private void dispatchDiagnosticData(String data) { HashMap sinks; synchronized(mSinkList) { sinks = mSinkList; } for(Map.Entry entry : sinks.entrySet()) { Message message = Message.obtain( entry.getValue(), DispatcherHandler.DISPATCH_DIAGNOSTIC_DATA, new MessageCommand(entry.getKey(), null /*locations*/, data, 0, 0)); message.sendToTarget(); } } private void dispatchCapabilities(int capabilities) { HashMap sinks; synchronized(mSinkList) { sinks = mSinkList; } for(Map.Entry entry : sinks.entrySet()) { Message message = Message.obtain( entry.getValue(), DispatcherHandler.DISPATCH_CAPABILITIES, new MessageCommand(entry.getKey(), null /*locations*/, null, capabilities, 0)); message.sendToTarget(); } } private void dispatchStatus(int status) { HashMap sinks; synchronized(mSinkList) { sinks = mSinkList; } for(Map.Entry entry : sinks.entrySet()) { Message message = Message.obtain( entry.getValue(), DispatcherHandler.DISPATCH_STATUS, new MessageCommand(entry.getKey(), null /*locations*/, null, 0, status)); message.sendToTarget(); } } }