1 /* 2 * Copyright 2017 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.server.location; 18 19 import android.content.Context; 20 import android.hardware.contexthub.V1_0.ContextHubMsg; 21 import android.hardware.contexthub.V1_0.IContexthub; 22 import android.hardware.contexthub.V1_0.Result; 23 import android.hardware.location.ContextHubTransaction; 24 import android.hardware.location.IContextHubClient; 25 import android.hardware.location.IContextHubClientCallback; 26 import android.hardware.location.NanoAppMessage; 27 import android.os.IBinder; 28 import android.os.RemoteException; 29 import android.util.Log; 30 31 import java.util.concurrent.atomic.AtomicBoolean; 32 33 /** 34 * A class that acts as a broker for the ContextHubClient, which handles messaging and life-cycle 35 * notification callbacks. This class implements the IContextHubClient object, and the implemented 36 * APIs must be thread-safe. 37 * 38 * @hide 39 */ 40 public class ContextHubClientBroker extends IContextHubClient.Stub 41 implements IBinder.DeathRecipient { 42 private static final String TAG = "ContextHubClientBroker"; 43 44 /* 45 * The context of the service. 46 */ 47 private final Context mContext; 48 49 /* 50 * The proxy to talk to the Context Hub HAL. 51 */ 52 private final IContexthub mContextHubProxy; 53 54 /* 55 * The manager that registered this client. 56 */ 57 private final ContextHubClientManager mClientManager; 58 59 /* 60 * The ID of the hub that this client is attached to. 61 */ 62 private final int mAttachedContextHubId; 63 64 /* 65 * The host end point ID of this client. 66 */ 67 private final short mHostEndPointId; 68 69 /* 70 * The remote callback interface for this client. 71 */ 72 private final IContextHubClientCallback mCallbackInterface; 73 74 /* 75 * false if the connection has been closed by the client, true otherwise. 76 */ 77 private final AtomicBoolean mConnectionOpen = new AtomicBoolean(true); 78 79 /* package */ ContextHubClientBroker( 80 Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager, 81 int contextHubId, short hostEndPointId, IContextHubClientCallback callback) { 82 mContext = context; 83 mContextHubProxy = contextHubProxy; 84 mClientManager = clientManager; 85 mAttachedContextHubId = contextHubId; 86 mHostEndPointId = hostEndPointId; 87 mCallbackInterface = callback; 88 } 89 90 /** 91 * Attaches a death recipient for this client 92 * 93 * @throws RemoteException if the client has already died 94 */ 95 /* package */ void attachDeathRecipient() throws RemoteException { 96 mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */); 97 } 98 99 /** 100 * Sends from this client to a nanoapp. 101 * 102 * @param message the message to send 103 * @return the error code of sending the message 104 */ 105 @ContextHubTransaction.Result 106 @Override 107 public int sendMessageToNanoApp(NanoAppMessage message) { 108 ContextHubServiceUtil.checkPermissions(mContext); 109 110 int result; 111 if (mConnectionOpen.get()) { 112 ContextHubMsg messageToNanoApp = ContextHubServiceUtil.createHidlContextHubMessage( 113 mHostEndPointId, message); 114 115 try { 116 result = mContextHubProxy.sendMessageToHub(mAttachedContextHubId, messageToNanoApp); 117 } catch (RemoteException e) { 118 Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = " 119 + mAttachedContextHubId + ")", e); 120 result = Result.UNKNOWN_FAILURE; 121 } 122 } else { 123 Log.e(TAG, "Failed to send message to nanoapp: client connection is closed"); 124 result = Result.UNKNOWN_FAILURE; 125 } 126 127 return ContextHubServiceUtil.toTransactionResult(result); 128 } 129 130 /** 131 * Closes the connection for this client with the service. 132 */ 133 @Override 134 public void close() { 135 if (mConnectionOpen.getAndSet(false)) { 136 mClientManager.unregisterClient(mHostEndPointId); 137 } 138 } 139 140 /** 141 * Invoked when the underlying binder of this broker has died at the client process. 142 */ 143 public void binderDied() { 144 try { 145 IContextHubClient.Stub.asInterface(this).close(); 146 } catch (RemoteException e) { 147 Log.e(TAG, "RemoteException while closing client on death", e); 148 } 149 } 150 151 /** 152 * @return the ID of the context hub this client is attached to 153 */ 154 /* package */ int getAttachedContextHubId() { 155 return mAttachedContextHubId; 156 } 157 158 /** 159 * @return the host endpoint ID of this client 160 */ 161 /* package */ short getHostEndPointId() { 162 return mHostEndPointId; 163 } 164 165 /** 166 * Sends a message to the client associated with this object. 167 * 168 * @param message the message that came from a nanoapp 169 */ 170 /* package */ void sendMessageToClient(NanoAppMessage message) { 171 if (mConnectionOpen.get()) { 172 try { 173 mCallbackInterface.onMessageFromNanoApp(message); 174 } catch (RemoteException e) { 175 Log.e(TAG, "RemoteException while sending message to client (host endpoint ID = " 176 + mHostEndPointId + ")", e); 177 } 178 } 179 } 180 181 /** 182 * Notifies the client of a nanoapp load event if the connection is open. 183 * 184 * @param nanoAppId the ID of the nanoapp that was loaded. 185 */ 186 /* package */ void onNanoAppLoaded(long nanoAppId) { 187 if (mConnectionOpen.get()) { 188 try { 189 mCallbackInterface.onNanoAppLoaded(nanoAppId); 190 } catch (RemoteException e) { 191 Log.e(TAG, "RemoteException while calling onNanoAppLoaded on client" 192 + " (host endpoint ID = " + mHostEndPointId + ")", e); 193 } 194 } 195 } 196 197 /** 198 * Notifies the client of a nanoapp unload event if the connection is open. 199 * 200 * @param nanoAppId the ID of the nanoapp that was unloaded. 201 */ 202 /* package */ void onNanoAppUnloaded(long nanoAppId) { 203 if (mConnectionOpen.get()) { 204 try { 205 mCallbackInterface.onNanoAppUnloaded(nanoAppId); 206 } catch (RemoteException e) { 207 Log.e(TAG, "RemoteException while calling onNanoAppUnloaded on client" 208 + " (host endpoint ID = " + mHostEndPointId + ")", e); 209 } 210 } 211 } 212 213 /** 214 * Notifies the client of a hub reset event if the connection is open. 215 */ 216 /* package */ void onHubReset() { 217 if (mConnectionOpen.get()) { 218 try { 219 mCallbackInterface.onHubReset(); 220 } catch (RemoteException e) { 221 Log.e(TAG, "RemoteException while calling onHubReset on client" + 222 " (host endpoint ID = " + mHostEndPointId + ")", e); 223 } 224 } 225 } 226 227 /** 228 * Notifies the client of a nanoapp abort event if the connection is open. 229 * 230 * @param nanoAppId the ID of the nanoapp that aborted 231 * @param abortCode the nanoapp specific abort code 232 */ 233 /* package */ void onNanoAppAborted(long nanoAppId, int abortCode) { 234 if (mConnectionOpen.get()) { 235 try { 236 mCallbackInterface.onNanoAppAborted(nanoAppId, abortCode); 237 } catch (RemoteException e) { 238 Log.e(TAG, "RemoteException while calling onNanoAppAborted on client" 239 + " (host endpoint ID = " + mHostEndPointId + ")", e); 240 } 241 } 242 } 243 } 244