blob: 00b7d62738c0989ae4e0e45a219bca84f7cdc920 [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 Ishigurod3464c72018-10-16 11:08:28 -070019import android.app.PendingIntent;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080020import android.content.Context;
21import android.hardware.contexthub.V1_0.ContextHubMsg;
22import android.hardware.contexthub.V1_0.IContexthub;
Arthur Ishiguro622ebcb2018-10-17 14:02:27 -070023import android.hardware.location.ContextHubInfo;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080024import android.hardware.location.IContextHubClient;
25import android.hardware.location.IContextHubClientCallback;
26import android.hardware.location.NanoAppMessage;
Arthur Ishigurob5492862018-11-13 10:51:15 -080027import android.os.RemoteException;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080028import android.util.Log;
29
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080030import java.util.concurrent.ConcurrentHashMap;
Arthur Ishiguro6d47c542017-11-17 15:49:07 -080031import java.util.function.Consumer;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080032
33/**
34 * A class that manages registration/unregistration of clients and manages messages to/from clients.
35 *
36 * @hide
37 */
38/* package */ class ContextHubClientManager {
39 private static final String TAG = "ContextHubClientManager";
40
41 /*
42 * The maximum host endpoint ID value that a client can be assigned.
43 */
44 private static final int MAX_CLIENT_ID = 0x7fff;
45
46 /*
47 * Local flag to enable debug logging.
48 */
Arthur Ishigurob4f59872018-07-31 16:38:08 -070049 private static final boolean DEBUG_LOG_ENABLED = false;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080050
51 /*
52 * The context of the service.
53 */
54 private final Context mContext;
55
56 /*
57 * The proxy to talk to the Context Hub.
58 */
59 private final IContexthub mContextHubProxy;
60
61 /*
62 * A mapping of host endpoint IDs to the ContextHubClientBroker object of registered clients.
63 * A concurrent data structure is used since the registration/unregistration can occur in
64 * multiple threads.
65 */
66 private final ConcurrentHashMap<Short, ContextHubClientBroker> mHostEndPointIdToClientMap =
67 new ConcurrentHashMap<>();
68
69 /*
70 * The next host endpoint ID to start iterating for the next available host endpoint ID.
71 */
Arthur Ishigurob5492862018-11-13 10:51:15 -080072 private int mNextHostEndPointId = 0;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080073
74 /* package */ ContextHubClientManager(
75 Context context, IContexthub contextHubProxy) {
76 mContext = context;
77 mContextHubProxy = contextHubProxy;
78 }
79
80 /**
81 * Registers a new client with the service.
82 *
Arthur Ishiguro622ebcb2018-10-17 14:02:27 -070083 * @param contextHubInfo the object describing the hub this client is attached to
Arthur Ishiguro793d3d12018-11-15 13:10:24 -080084 * @param clientCallback the callback interface of the client to register
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -080085 *
86 * @return the client interface
87 *
88 * @throws IllegalStateException if max number of clients have already registered
89 */
90 /* package */ IContextHubClient registerClient(
Arthur Ishiguro793d3d12018-11-15 13:10:24 -080091 ContextHubInfo contextHubInfo, IContextHubClientCallback clientCallback) {
Arthur Ishigurob5492862018-11-13 10:51:15 -080092 ContextHubClientBroker broker;
93 synchronized (this) {
94 short hostEndPointId = getHostEndPointId();
95 broker = new ContextHubClientBroker(
96 mContext, mContextHubProxy, this /* clientManager */, contextHubInfo,
97 hostEndPointId, clientCallback);
98 mHostEndPointIdToClientMap.put(hostEndPointId, broker);
99 }
100
101 try {
102 broker.attachDeathRecipient();
103 } catch (RemoteException e) {
104 // The client process has died, so we close the connection and return null
105 Log.e(TAG, "Failed to attach death recipient to client");
106 broker.close();
107 return null;
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800108 }
109
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800110 Log.d(TAG, "Registered client with host endpoint ID " + broker.getHostEndPointId());
111 return IContextHubClient.Stub.asInterface(broker);
112 }
113
114 /**
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800115 * Registers a new client with the service.
116 *
117 * @param pendingIntent the callback interface of the client to register
118 * @param contextHubInfo the object describing the hub this client is attached to
119 * @param nanoAppId the ID of the nanoapp to receive Intent events for
120 *
121 * @return the client interface
122 *
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800123 * @throws IllegalStateException if there were too many registered clients at the service
124 */
125 /* package */ IContextHubClient registerClient(
126 ContextHubInfo contextHubInfo, PendingIntent pendingIntent, long nanoAppId) {
127 ContextHubClientBroker broker;
128 String registerString = "Regenerated";
129 synchronized (this) {
130 broker = getClientBroker(contextHubInfo.getId(), pendingIntent, nanoAppId);
131
132 if (broker == null) {
133 short hostEndPointId = getHostEndPointId();
134 broker = new ContextHubClientBroker(
135 mContext, mContextHubProxy, this /* clientManager */, contextHubInfo,
136 hostEndPointId, pendingIntent, nanoAppId);
137 mHostEndPointIdToClientMap.put(hostEndPointId, broker);
138 registerString = "Registered";
139 }
140 }
141
142 Log.d(TAG, registerString + " client with host endpoint ID " + broker.getHostEndPointId());
143 return IContextHubClient.Stub.asInterface(broker);
144 }
145
146 /**
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800147 * Handles a message sent from a nanoapp.
148 *
149 * @param contextHubId the ID of the hub where the nanoapp sent the message from
150 * @param message the message send by a nanoapp
151 */
152 /* package */ void onMessageFromNanoApp(int contextHubId, ContextHubMsg message) {
153 NanoAppMessage clientMessage = ContextHubServiceUtil.createNanoAppMessage(message);
154
155 if (DEBUG_LOG_ENABLED) {
Arthur Ishiguro1d3b23c2018-01-05 16:05:25 -0800156 Log.v(TAG, "Received " + clientMessage);
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800157 }
158
159 if (clientMessage.isBroadcastMessage()) {
160 broadcastMessage(contextHubId, clientMessage);
161 } else {
162 ContextHubClientBroker proxy = mHostEndPointIdToClientMap.get(message.hostEndPoint);
163 if (proxy != null) {
164 proxy.sendMessageToClient(clientMessage);
165 } else {
166 Log.e(TAG, "Cannot send message to unregistered client (host endpoint ID = "
167 + message.hostEndPoint + ")");
168 }
169 }
170 }
171
172 /**
Arthur Ishiguro49e030f2017-11-16 11:54:42 -0800173 * Unregisters a client from the service.
174 *
175 * This method should be invoked as a result of a client calling the ContextHubClient.close(),
176 * or if the client process has died.
177 *
178 * @param hostEndPointId the host endpoint ID of the client that has died
179 */
180 /* package */ void unregisterClient(short hostEndPointId) {
181 if (mHostEndPointIdToClientMap.remove(hostEndPointId) != null) {
182 Log.d(TAG, "Unregistered client with host endpoint ID " + hostEndPointId);
183 } else {
184 Log.e(TAG, "Cannot unregister non-existing client with host endpoint ID "
185 + hostEndPointId);
186 }
187 }
188
189 /**
Arthur Ishiguro02ff50b2017-12-18 10:02:35 -0800190 * @param contextHubId the ID of the hub where the nanoapp was loaded
191 * @param nanoAppId the ID of the nanoapp that was loaded
Arthur Ishiguro0069f122017-11-20 15:07:14 -0800192 */
193 /* package */ void onNanoAppLoaded(int contextHubId, long nanoAppId) {
194 forEachClientOfHub(contextHubId, client -> client.onNanoAppLoaded(nanoAppId));
195 }
196
197 /**
Arthur Ishiguro02ff50b2017-12-18 10:02:35 -0800198 * @param contextHubId the ID of the hub where the nanoapp was unloaded
199 * @param nanoAppId the ID of the nanoapp that was unloaded
Arthur Ishiguro0069f122017-11-20 15:07:14 -0800200 */
201 /* package */ void onNanoAppUnloaded(int contextHubId, long nanoAppId) {
202 forEachClientOfHub(contextHubId, client -> client.onNanoAppUnloaded(nanoAppId));
203 }
204
205 /**
Arthur Ishiguro02ff50b2017-12-18 10:02:35 -0800206 * @param contextHubId the ID of the hub that has reset
Arthur Ishiguro6d47c542017-11-17 15:49:07 -0800207 */
208 /* package */ void onHubReset(int contextHubId) {
209 forEachClientOfHub(contextHubId, client -> client.onHubReset());
210 }
211
212 /**
Arthur Ishiguro02ff50b2017-12-18 10:02:35 -0800213 * @param contextHubId the ID of the hub that contained the nanoapp that aborted
214 * @param nanoAppId the ID of the nanoapp that aborted
215 * @param abortCode the nanoapp specific abort code
216 */
217 /* package */ void onNanoAppAborted(int contextHubId, long nanoAppId, int abortCode) {
218 forEachClientOfHub(contextHubId, client -> client.onNanoAppAborted(nanoAppId, abortCode));
219 }
220
221 /**
Arthur Ishigurob5492862018-11-13 10:51:15 -0800222 * Returns an available host endpoint ID.
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800223 *
Arthur Ishigurob5492862018-11-13 10:51:15 -0800224 * @returns an available host endpoint ID
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800225 *
226 * @throws IllegalStateException if max number of clients have already registered
227 */
Arthur Ishigurob5492862018-11-13 10:51:15 -0800228 private short getHostEndPointId() {
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800229 if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
230 throw new IllegalStateException("Could not register client - max limit exceeded");
231 }
232
Arthur Ishigurob5492862018-11-13 10:51:15 -0800233 int id = mNextHostEndPointId;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800234 for (int i = 0; i <= MAX_CLIENT_ID; i++) {
Arthur Ishiguro622ebcb2018-10-17 14:02:27 -0700235 if (!mHostEndPointIdToClientMap.containsKey((short) id)) {
Arthur Ishigurob5492862018-11-13 10:51:15 -0800236 mNextHostEndPointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800237 break;
238 }
239
240 id = (id == MAX_CLIENT_ID) ? 0 : id + 1;
241 }
242
Arthur Ishigurob5492862018-11-13 10:51:15 -0800243 return (short) id;
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800244 }
245
246 /**
247 * Broadcasts a message from a nanoapp to all clients attached to the associated hub.
248 *
249 * @param contextHubId the ID of the hub where the nanoapp sent the message from
250 * @param message the message send by a nanoapp
251 */
252 private void broadcastMessage(int contextHubId, NanoAppMessage message) {
Arthur Ishiguro6d47c542017-11-17 15:49:07 -0800253 forEachClientOfHub(contextHubId, client -> client.sendMessageToClient(message));
254 }
255
256 /**
257 * Runs a command for each client that is attached to a hub with the given ID.
258 *
259 * @param contextHubId the ID of the hub
260 * @param callback the command to invoke for the client
261 */
262 private void forEachClientOfHub(int contextHubId, Consumer<ContextHubClientBroker> callback) {
263 for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
264 if (broker.getAttachedContextHubId() == contextHubId) {
265 callback.accept(broker);
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800266 }
267 }
268 }
Arthur Ishigurod3464c72018-10-16 11:08:28 -0700269
270 /**
271 * Retrieves a ContextHubClientBroker object with a matching PendingIntent and Context Hub ID.
272 *
273 * @param pendingIntent the PendingIntent to match
274 * @param contextHubId the ID of the Context Hub the client is attached to
275 * @return the matching ContextHubClientBroker, null if not found
276 */
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800277 private ContextHubClientBroker getClientBroker(
278 int contextHubId, PendingIntent pendingIntent, long nanoAppId) {
Arthur Ishigurod3464c72018-10-16 11:08:28 -0700279 for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
Arthur Ishiguro82ed3af2018-11-09 13:20:30 -0800280 if (broker.hasPendingIntent(pendingIntent, nanoAppId)
Arthur Ishigurod3464c72018-10-16 11:08:28 -0700281 && broker.getAttachedContextHubId() == contextHubId) {
282 return broker;
283 }
284 }
285
286 return null;
287 }
Arthur Ishiguro4e39aa12017-11-14 14:59:08 -0800288}