blob: 6612d0264affe5ff9894ebac6fbad7e0cf9fc82c [file] [log] [blame]
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -08001/*
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
17package com.android.server.location;
18
Arthur Ishiguro9aa88e02018-10-16 10:26:57 -070019import android.Manifest;
Arthur Ishiguroa7c37972018-10-01 15:42:27 -070020import android.app.PendingIntent;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080021import android.content.Context;
Arthur Ishiguro9aa88e02018-10-16 10:26:57 -070022import android.content.Intent;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080023import android.hardware.contexthub.V1_0.ContextHubMsg;
24import android.hardware.contexthub.V1_0.IContexthub;
25import android.hardware.contexthub.V1_0.Result;
Arthur Ishiguro622ebcb2018-10-17 14:02:27 -070026import android.hardware.location.ContextHubInfo;
Arthur Ishiguro9aa88e02018-10-16 10:26:57 -070027import android.hardware.location.ContextHubManager;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080028import android.hardware.location.ContextHubTransaction;
29import android.hardware.location.IContextHubClient;
30import android.hardware.location.IContextHubClientCallback;
31import android.hardware.location.NanoAppMessage;
Arthur Ishiguro49e030f2017-11-16 11:54:42 -080032import android.os.IBinder;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080033import android.os.RemoteException;
34import android.util.Log;
35
Arthur Ishiguro9aa88e02018-10-16 10:26:57 -070036import java.util.function.Supplier;
37
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080038/**
Arthur Ishiguro49e030f2017-11-16 11:54:42 -080039 * A class that acts as a broker for the ContextHubClient, which handles messaging and life-cycle
40 * notification callbacks. This class implements the IContextHubClient object, and the implemented
41 * APIs must be thread-safe.
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080042 *
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -080043 * TODO: Consider refactoring this class via inheritance
44 *
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080045 * @hide
46 */
Arthur Ishiguro49e030f2017-11-16 11:54:42 -080047public class ContextHubClientBroker extends IContextHubClient.Stub
48 implements IBinder.DeathRecipient {
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080049 private static final String TAG = "ContextHubClientBroker";
50
51 /*
52 * The context of the service.
53 */
54 private final Context mContext;
55
56 /*
57 * The proxy to talk to the Context Hub HAL.
58 */
59 private final IContexthub mContextHubProxy;
60
61 /*
Arthur Ishiguro49e030f2017-11-16 11:54:42 -080062 * The manager that registered this client.
63 */
64 private final ContextHubClientManager mClientManager;
65
66 /*
Arthur Ishiguro622ebcb2018-10-17 14:02:27 -070067 * The object describing the hub that this client is attached to.
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080068 */
Arthur Ishiguro622ebcb2018-10-17 14:02:27 -070069 private final ContextHubInfo mAttachedContextHubInfo;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080070
71 /*
72 * The host end point ID of this client.
73 */
74 private final short mHostEndPointId;
75
76 /*
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -070077 * The remote callback interface for this client. This will be set to null whenever the
78 * client connection is closed (either explicitly or via binder death).
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080079 */
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -070080 private IContextHubClientCallback mCallbackInterface = null;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080081
Arthur Ishiguro49e030f2017-11-16 11:54:42 -080082 /*
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -070083 * True if the client is still registered with the Context Hub Service, false otherwise.
Arthur Ishiguro49e030f2017-11-16 11:54:42 -080084 */
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -070085 private boolean mRegistered = true;
Arthur Ishiguro49e030f2017-11-16 11:54:42 -080086
Arthur Ishiguro661f5022018-10-10 12:17:29 -070087 /*
88 * Internal interface used to invoke client callbacks.
89 */
90 private interface CallbackConsumer {
91 void accept(IContextHubClientCallback callback) throws RemoteException;
92 }
93
Arthur Ishiguro32c2bc62018-10-16 10:26:11 -070094 /*
95 * The PendingIntent registered with this client.
96 */
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -080097 private final PendingIntentRequest mPendingIntentRequest;
Arthur Ishiguro32c2bc62018-10-16 10:26:11 -070098
99 /*
100 * Helper class to manage registered PendingIntent requests from the client.
101 */
102 private class PendingIntentRequest {
103 /*
104 * The PendingIntent object to request, null if there is no open request.
105 */
106 private PendingIntent mPendingIntent;
107
108 /*
109 * The ID of the nanoapp the request is for, invalid if there is no open request.
110 */
111 private long mNanoAppId;
112
113 PendingIntentRequest() {}
114
115 PendingIntentRequest(PendingIntent pendingIntent, long nanoAppId) {
116 mPendingIntent = pendingIntent;
117 mNanoAppId = nanoAppId;
118 }
119
120 public long getNanoAppId() {
121 return mNanoAppId;
122 }
123
124 public PendingIntent getPendingIntent() {
125 return mPendingIntent;
126 }
127
128 public boolean hasPendingIntent() {
129 return mPendingIntent != null;
130 }
131
132 public void clear() {
133 mPendingIntent = null;
134 }
Arthur Ishiguro32c2bc62018-10-16 10:26:11 -0700135 }
136
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800137 /* package */ ContextHubClientBroker(
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800138 Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
Arthur Ishigurob5492862018-11-13 10:51:15 -0800139 ContextHubInfo contextHubInfo, short hostEndPointId,
140 IContextHubClientCallback callback) {
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800141 mContext = context;
142 mContextHubProxy = contextHubProxy;
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800143 mClientManager = clientManager;
Arthur Ishiguro622ebcb2018-10-17 14:02:27 -0700144 mAttachedContextHubInfo = contextHubInfo;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800145 mHostEndPointId = hostEndPointId;
Arthur Ishigurob5492862018-11-13 10:51:15 -0800146 mCallbackInterface = callback;
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800147 mPendingIntentRequest = new PendingIntentRequest();
148 }
149
150 /* package */ ContextHubClientBroker(
151 Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
152 ContextHubInfo contextHubInfo, short hostEndPointId, PendingIntent pendingIntent,
153 long nanoAppId) {
154 mContext = context;
155 mContextHubProxy = contextHubProxy;
156 mClientManager = clientManager;
157 mAttachedContextHubInfo = contextHubInfo;
158 mHostEndPointId = hostEndPointId;
159 mPendingIntentRequest = new PendingIntentRequest(pendingIntent, nanoAppId);
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800160 }
161
162 /**
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800163 * Sends from this client to a nanoapp.
164 *
165 * @param message the message to send
166 * @return the error code of sending the message
167 */
168 @ContextHubTransaction.Result
169 @Override
170 public int sendMessageToNanoApp(NanoAppMessage message) {
171 ContextHubServiceUtil.checkPermissions(mContext);
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800172
173 int result;
Arthur Ishiguroa96fbfe2018-11-13 10:00:35 -0800174 if (isRegistered()) {
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -0700175 ContextHubMsg messageToNanoApp =
176 ContextHubServiceUtil.createHidlContextHubMessage(mHostEndPointId, message);
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800177
Arthur Ishiguro622ebcb2018-10-17 14:02:27 -0700178 int contextHubId = mAttachedContextHubInfo.getId();
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800179 try {
Arthur Ishiguro622ebcb2018-10-17 14:02:27 -0700180 result = mContextHubProxy.sendMessageToHub(contextHubId, messageToNanoApp);
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800181 } catch (RemoteException e) {
182 Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = "
Arthur Ishiguro622ebcb2018-10-17 14:02:27 -0700183 + contextHubId + ")", e);
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800184 result = Result.UNKNOWN_FAILURE;
185 }
186 } else {
187 Log.e(TAG, "Failed to send message to nanoapp: client connection is closed");
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800188 result = Result.UNKNOWN_FAILURE;
189 }
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800190
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800191 return ContextHubServiceUtil.toTransactionResult(result);
192 }
193
194 /**
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800195 * Closes the connection for this client with the service.
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800196 *
197 * If the client has a PendingIntent registered, this method also unregisters it.
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800198 */
199 @Override
200 public void close() {
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -0700201 synchronized (this) {
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800202 mPendingIntentRequest.clear();
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800203 }
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800204 onClientExit();
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800205 }
206
207 /**
208 * Invoked when the underlying binder of this broker has died at the client process.
209 */
Arthur Ishiguro9f6284e2018-10-10 11:09:02 -0700210 @Override
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800211 public void binderDied() {
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800212 onClientExit();
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800213 }
214
215 /**
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800216 * @return the ID of the context hub this client is attached to
217 */
218 /* package */ int getAttachedContextHubId() {
Arthur Ishiguro622ebcb2018-10-17 14:02:27 -0700219 return mAttachedContextHubInfo.getId();
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800220 }
221
222 /**
223 * @return the host endpoint ID of this client
224 */
225 /* package */ short getHostEndPointId() {
226 return mHostEndPointId;
227 }
228
229 /**
230 * Sends a message to the client associated with this object.
231 *
232 * @param message the message that came from a nanoapp
233 */
234 /* package */ void sendMessageToClient(NanoAppMessage message) {
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -0700235 invokeCallback(callback -> callback.onMessageFromNanoApp(message));
Arthur Ishiguro9aa88e02018-10-16 10:26:57 -0700236
237 Supplier<Intent> supplier =
238 () -> createIntent(ContextHubManager.EVENT_NANOAPP_MESSAGE, message.getNanoAppId())
239 .putExtra(ContextHubManager.EXTRA_MESSAGE, message);
240 sendPendingIntent(supplier);
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800241 }
Arthur Ishiguro6d47c542017-11-17 15:49:07 -0800242
243 /**
Arthur Ishiguro02ff50b2017-12-18 10:02:35 -0800244 * Notifies the client of a nanoapp load event if the connection is open.
Arthur Ishiguro0069f122017-11-20 15:07:14 -0800245 *
246 * @param nanoAppId the ID of the nanoapp that was loaded.
247 */
248 /* package */ void onNanoAppLoaded(long nanoAppId) {
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -0700249 invokeCallback(callback -> callback.onNanoAppLoaded(nanoAppId));
Arthur Ishiguro9aa88e02018-10-16 10:26:57 -0700250 sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_NANOAPP_LOADED, nanoAppId));
Arthur Ishiguro0069f122017-11-20 15:07:14 -0800251 }
252
253 /**
Arthur Ishiguro02ff50b2017-12-18 10:02:35 -0800254 * Notifies the client of a nanoapp unload event if the connection is open.
Arthur Ishiguro0069f122017-11-20 15:07:14 -0800255 *
256 * @param nanoAppId the ID of the nanoapp that was unloaded.
257 */
258 /* package */ void onNanoAppUnloaded(long nanoAppId) {
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -0700259 invokeCallback(callback -> callback.onNanoAppUnloaded(nanoAppId));
Arthur Ishiguro9aa88e02018-10-16 10:26:57 -0700260 sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_NANOAPP_UNLOADED, nanoAppId));
Arthur Ishiguro0069f122017-11-20 15:07:14 -0800261 }
262
263 /**
Arthur Ishiguro02ff50b2017-12-18 10:02:35 -0800264 * Notifies the client of a hub reset event if the connection is open.
Arthur Ishiguro6d47c542017-11-17 15:49:07 -0800265 */
266 /* package */ void onHubReset() {
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -0700267 invokeCallback(callback -> callback.onHubReset());
Arthur Ishiguro9aa88e02018-10-16 10:26:57 -0700268 sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_HUB_RESET));
Arthur Ishiguro6d47c542017-11-17 15:49:07 -0800269 }
Arthur Ishiguro02ff50b2017-12-18 10:02:35 -0800270
271 /**
272 * Notifies the client of a nanoapp abort event if the connection is open.
273 *
274 * @param nanoAppId the ID of the nanoapp that aborted
275 * @param abortCode the nanoapp specific abort code
276 */
277 /* package */ void onNanoAppAborted(long nanoAppId, int abortCode) {
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -0700278 invokeCallback(callback -> callback.onNanoAppAborted(nanoAppId, abortCode));
Arthur Ishiguro9aa88e02018-10-16 10:26:57 -0700279
280 Supplier<Intent> supplier =
281 () -> createIntent(ContextHubManager.EVENT_NANOAPP_ABORTED, nanoAppId)
282 .putExtra(ContextHubManager.EXTRA_NANOAPP_ABORT_CODE, abortCode);
283 sendPendingIntent(supplier);
Arthur Ishiguro661f5022018-10-10 12:17:29 -0700284 }
285
286 /**
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800287 * @param intent the PendingIntent to compare to
288 * @param nanoAppId the ID of the nanoapp of the PendingIntent to compare to
Arthur Ishigurod3464c72018-10-16 11:08:28 -0700289 * @return true if the given PendingIntent is currently registered, false otherwise
290 */
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800291 /* package */ boolean hasPendingIntent(PendingIntent intent, long nanoAppId) {
Arthur Ishigurod3464c72018-10-16 11:08:28 -0700292 PendingIntent pendingIntent = null;
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800293 long intentNanoAppId;
Arthur Ishigurod3464c72018-10-16 11:08:28 -0700294 synchronized (this) {
295 pendingIntent = mPendingIntentRequest.getPendingIntent();
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800296 intentNanoAppId = mPendingIntentRequest.getNanoAppId();
Arthur Ishigurod3464c72018-10-16 11:08:28 -0700297 }
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800298 return (pendingIntent != null) && pendingIntent.equals(intent)
299 && intentNanoAppId == nanoAppId;
Arthur Ishigurod3464c72018-10-16 11:08:28 -0700300 }
301
302 /**
Arthur Ishigurob5492862018-11-13 10:51:15 -0800303 * Attaches the death recipient to the callback interface object, if any.
304 *
305 * @throws RemoteException if the client process already died
306 */
307 /* package */ void attachDeathRecipient() throws RemoteException {
308 if (mCallbackInterface != null) {
309 mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
310 }
311 }
312
313 /**
Arthur Ishiguro661f5022018-10-10 12:17:29 -0700314 * Helper function to invoke a specified client callback, if the connection is open.
315 *
316 * @param consumer the consumer specifying the callback to invoke
317 */
Arthur Ishigurodaeed3c2018-10-01 15:42:27 -0700318 private synchronized void invokeCallback(CallbackConsumer consumer) {
319 if (mCallbackInterface != null) {
Arthur Ishiguro02ff50b2017-12-18 10:02:35 -0800320 try {
Arthur Ishiguro661f5022018-10-10 12:17:29 -0700321 consumer.accept(mCallbackInterface);
Arthur Ishiguro02ff50b2017-12-18 10:02:35 -0800322 } catch (RemoteException e) {
Arthur Ishiguro661f5022018-10-10 12:17:29 -0700323 Log.e(TAG, "RemoteException while invoking client callback (host endpoint ID = "
324 + mHostEndPointId + ")", e);
Arthur Ishiguro02ff50b2017-12-18 10:02:35 -0800325 }
326 }
327 }
Arthur Ishiguro9aa88e02018-10-16 10:26:57 -0700328
329 /**
330 * Creates an Intent object containing the ContextHubManager.EXTRA_EVENT_TYPE extra field
331 *
332 * @param eventType the ContextHubManager.Event type describing the event
333 * @return the Intent object
334 */
335 private Intent createIntent(int eventType) {
336 Intent intent = new Intent();
337 intent.putExtra(ContextHubManager.EXTRA_EVENT_TYPE, eventType);
338 intent.putExtra(ContextHubManager.EXTRA_CONTEXT_HUB_INFO, mAttachedContextHubInfo);
339 return intent;
340 }
341
342 /**
343 * Creates an Intent object containing the ContextHubManager.EXTRA_EVENT_TYPE and the
344 * ContextHubManager.EXTRA_NANOAPP_ID extra fields
345 *
346 * @param eventType the ContextHubManager.Event type describing the event
347 * @param nanoAppId the ID of the nanoapp this event is for
348 * @return the Intent object
349 */
350 private Intent createIntent(int eventType, long nanoAppId) {
351 Intent intent = createIntent(eventType);
352 intent.putExtra(ContextHubManager.EXTRA_NANOAPP_ID, nanoAppId);
353 return intent;
354 }
355
356 /**
357 * Sends an intent to any existing PendingIntent
358 *
359 * @param supplier method to create the extra Intent
360 */
361 private synchronized void sendPendingIntent(Supplier<Intent> supplier) {
362 if (mPendingIntentRequest.hasPendingIntent()) {
363 Intent intent = supplier.get();
364 try {
365 mPendingIntentRequest.getPendingIntent().send(
366 mContext, 0 /* code */, intent, null /* onFinished */, null /* Handler */,
367 Manifest.permission.LOCATION_HARDWARE /* requiredPermission */,
368 null /* options */);
369 } catch (PendingIntent.CanceledException e) {
370 // The PendingIntent is no longer valid
371 Log.w(TAG, "PendingIntent has been canceled, unregistering from client"
372 + " (host endpoint ID " + mHostEndPointId + ")");
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800373 close();
Arthur Ishiguro9aa88e02018-10-16 10:26:57 -0700374 }
375 }
376 }
Arthur Ishiguroa96fbfe2018-11-13 10:00:35 -0800377
378 /**
379 * @return true if the client is still registered with the service, false otherwise
380 */
381 private synchronized boolean isRegistered() {
382 return mRegistered;
383 }
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800384
385 /**
386 * Invoked when a client exits either explicitly or by binder death.
387 */
388 private synchronized void onClientExit() {
389 if (mCallbackInterface != null) {
390 mCallbackInterface.asBinder().unlinkToDeath(this, 0 /* flags */);
391 mCallbackInterface = null;
392 }
393 if (!mPendingIntentRequest.hasPendingIntent() && mRegistered) {
394 mClientManager.unregisterClient(mHostEndPointId);
395 mRegistered = false;
396 }
397 }
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800398}