Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 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 | |
Arthur Ishiguro | 0cf065e | 2020-01-22 17:05:26 +0000 | [diff] [blame] | 19 | import static android.content.pm.PackageManager.PERMISSION_GRANTED; |
| 20 | |
Arthur Ishiguro | 9aa88e0 | 2018-10-16 10:26:57 -0700 | [diff] [blame] | 21 | import android.Manifest; |
Arthur Ishiguro | a7c3797 | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 22 | import android.app.PendingIntent; |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 23 | import android.content.Context; |
Arthur Ishiguro | 9aa88e0 | 2018-10-16 10:26:57 -0700 | [diff] [blame] | 24 | import android.content.Intent; |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 25 | import android.hardware.contexthub.V1_0.ContextHubMsg; |
| 26 | import android.hardware.contexthub.V1_0.IContexthub; |
| 27 | import android.hardware.contexthub.V1_0.Result; |
Arthur Ishiguro | 622ebcb | 2018-10-17 14:02:27 -0700 | [diff] [blame] | 28 | import android.hardware.location.ContextHubInfo; |
Arthur Ishiguro | 9aa88e0 | 2018-10-16 10:26:57 -0700 | [diff] [blame] | 29 | import android.hardware.location.ContextHubManager; |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 30 | import android.hardware.location.ContextHubTransaction; |
| 31 | import android.hardware.location.IContextHubClient; |
| 32 | import android.hardware.location.IContextHubClientCallback; |
| 33 | import android.hardware.location.NanoAppMessage; |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 34 | import android.os.Binder; |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 35 | import android.os.IBinder; |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 36 | import android.os.RemoteException; |
| 37 | import android.util.Log; |
Mike Ma | ac0cae7 | 2020-01-15 17:26:24 -0800 | [diff] [blame] | 38 | import android.util.proto.ProtoOutputStream; |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 39 | |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 40 | import java.util.concurrent.atomic.AtomicBoolean; |
Arthur Ishiguro | 9aa88e0 | 2018-10-16 10:26:57 -0700 | [diff] [blame] | 41 | import java.util.function.Supplier; |
| 42 | |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 43 | /** |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 44 | * A class that acts as a broker for the ContextHubClient, which handles messaging and life-cycle |
| 45 | * notification callbacks. This class implements the IContextHubClient object, and the implemented |
| 46 | * APIs must be thread-safe. |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 47 | * |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 48 | * TODO: Consider refactoring this class via inheritance |
| 49 | * |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 50 | * @hide |
| 51 | */ |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 52 | public class ContextHubClientBroker extends IContextHubClient.Stub |
| 53 | implements IBinder.DeathRecipient { |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 54 | private static final String TAG = "ContextHubClientBroker"; |
| 55 | |
| 56 | /* |
| 57 | * The context of the service. |
| 58 | */ |
| 59 | private final Context mContext; |
| 60 | |
| 61 | /* |
| 62 | * The proxy to talk to the Context Hub HAL. |
| 63 | */ |
| 64 | private final IContexthub mContextHubProxy; |
| 65 | |
| 66 | /* |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 67 | * The manager that registered this client. |
| 68 | */ |
| 69 | private final ContextHubClientManager mClientManager; |
| 70 | |
| 71 | /* |
Arthur Ishiguro | 622ebcb | 2018-10-17 14:02:27 -0700 | [diff] [blame] | 72 | * The object describing the hub that this client is attached to. |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 73 | */ |
Arthur Ishiguro | 622ebcb | 2018-10-17 14:02:27 -0700 | [diff] [blame] | 74 | private final ContextHubInfo mAttachedContextHubInfo; |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 75 | |
| 76 | /* |
| 77 | * The host end point ID of this client. |
| 78 | */ |
| 79 | private final short mHostEndPointId; |
| 80 | |
| 81 | /* |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 82 | * The remote callback interface for this client. This will be set to null whenever the |
| 83 | * client connection is closed (either explicitly or via binder death). |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 84 | */ |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 85 | private IContextHubClientCallback mCallbackInterface = null; |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 86 | |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 87 | /* |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 88 | * True if the client is still registered with the Context Hub Service, false otherwise. |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 89 | */ |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 90 | private boolean mRegistered = true; |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 91 | |
Arthur Ishiguro | 661f502 | 2018-10-10 12:17:29 -0700 | [diff] [blame] | 92 | /* |
| 93 | * Internal interface used to invoke client callbacks. |
| 94 | */ |
| 95 | private interface CallbackConsumer { |
| 96 | void accept(IContextHubClientCallback callback) throws RemoteException; |
| 97 | } |
| 98 | |
Arthur Ishiguro | 32c2bc6 | 2018-10-16 10:26:11 -0700 | [diff] [blame] | 99 | /* |
| 100 | * The PendingIntent registered with this client. |
| 101 | */ |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 102 | private final PendingIntentRequest mPendingIntentRequest; |
Arthur Ishiguro | 32c2bc6 | 2018-10-16 10:26:11 -0700 | [diff] [blame] | 103 | |
| 104 | /* |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 105 | * The host package associated with this client. |
| 106 | */ |
| 107 | private final String mPackage; |
| 108 | |
| 109 | /* |
| 110 | * True if a PendingIntent has been cancelled. |
| 111 | */ |
| 112 | private AtomicBoolean mIsPendingIntentCancelled = new AtomicBoolean(false); |
| 113 | |
| 114 | /* |
Arthur Ishiguro | 0cf065e | 2020-01-22 17:05:26 +0000 | [diff] [blame] | 115 | * True if the application creating the client has the ACCESS_CONTEXT_HUB permission. |
| 116 | */ |
| 117 | private final boolean mHasAccessContextHubPermission; |
| 118 | |
| 119 | /* |
Arthur Ishiguro | 32c2bc6 | 2018-10-16 10:26:11 -0700 | [diff] [blame] | 120 | * Helper class to manage registered PendingIntent requests from the client. |
| 121 | */ |
| 122 | private class PendingIntentRequest { |
| 123 | /* |
| 124 | * The PendingIntent object to request, null if there is no open request. |
| 125 | */ |
| 126 | private PendingIntent mPendingIntent; |
| 127 | |
| 128 | /* |
| 129 | * The ID of the nanoapp the request is for, invalid if there is no open request. |
| 130 | */ |
| 131 | private long mNanoAppId; |
| 132 | |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 133 | private boolean mValid = false; |
| 134 | |
Arthur Ishiguro | 32c2bc6 | 2018-10-16 10:26:11 -0700 | [diff] [blame] | 135 | PendingIntentRequest() {} |
| 136 | |
| 137 | PendingIntentRequest(PendingIntent pendingIntent, long nanoAppId) { |
| 138 | mPendingIntent = pendingIntent; |
| 139 | mNanoAppId = nanoAppId; |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 140 | mValid = true; |
Arthur Ishiguro | 32c2bc6 | 2018-10-16 10:26:11 -0700 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | public long getNanoAppId() { |
| 144 | return mNanoAppId; |
| 145 | } |
| 146 | |
| 147 | public PendingIntent getPendingIntent() { |
| 148 | return mPendingIntent; |
| 149 | } |
| 150 | |
| 151 | public boolean hasPendingIntent() { |
| 152 | return mPendingIntent != null; |
| 153 | } |
| 154 | |
| 155 | public void clear() { |
| 156 | mPendingIntent = null; |
| 157 | } |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 158 | |
| 159 | public boolean isValid() { |
| 160 | return mValid; |
| 161 | } |
Arthur Ishiguro | 32c2bc6 | 2018-10-16 10:26:11 -0700 | [diff] [blame] | 162 | } |
| 163 | |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 164 | /* package */ ContextHubClientBroker( |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 165 | Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager, |
Arthur Ishiguro | b549286 | 2018-11-13 10:51:15 -0800 | [diff] [blame] | 166 | ContextHubInfo contextHubInfo, short hostEndPointId, |
| 167 | IContextHubClientCallback callback) { |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 168 | mContext = context; |
| 169 | mContextHubProxy = contextHubProxy; |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 170 | mClientManager = clientManager; |
Arthur Ishiguro | 622ebcb | 2018-10-17 14:02:27 -0700 | [diff] [blame] | 171 | mAttachedContextHubInfo = contextHubInfo; |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 172 | mHostEndPointId = hostEndPointId; |
Arthur Ishiguro | b549286 | 2018-11-13 10:51:15 -0800 | [diff] [blame] | 173 | mCallbackInterface = callback; |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 174 | mPendingIntentRequest = new PendingIntentRequest(); |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 175 | mPackage = mContext.getPackageManager().getNameForUid(Binder.getCallingUid()); |
Arthur Ishiguro | 0cf065e | 2020-01-22 17:05:26 +0000 | [diff] [blame] | 176 | |
| 177 | mHasAccessContextHubPermission = context.checkCallingPermission( |
| 178 | Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED; |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | /* package */ ContextHubClientBroker( |
| 182 | Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager, |
| 183 | ContextHubInfo contextHubInfo, short hostEndPointId, PendingIntent pendingIntent, |
| 184 | long nanoAppId) { |
| 185 | mContext = context; |
| 186 | mContextHubProxy = contextHubProxy; |
| 187 | mClientManager = clientManager; |
| 188 | mAttachedContextHubInfo = contextHubInfo; |
| 189 | mHostEndPointId = hostEndPointId; |
| 190 | mPendingIntentRequest = new PendingIntentRequest(pendingIntent, nanoAppId); |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 191 | mPackage = pendingIntent.getCreatorPackage(); |
Arthur Ishiguro | 0cf065e | 2020-01-22 17:05:26 +0000 | [diff] [blame] | 192 | |
| 193 | mHasAccessContextHubPermission = context.checkCallingPermission( |
| 194 | Manifest.permission.ACCESS_CONTEXT_HUB) == PERMISSION_GRANTED; |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | /** |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 198 | * Sends from this client to a nanoapp. |
| 199 | * |
| 200 | * @param message the message to send |
| 201 | * @return the error code of sending the message |
| 202 | */ |
| 203 | @ContextHubTransaction.Result |
| 204 | @Override |
| 205 | public int sendMessageToNanoApp(NanoAppMessage message) { |
| 206 | ContextHubServiceUtil.checkPermissions(mContext); |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 207 | |
| 208 | int result; |
Arthur Ishiguro | a96fbfe | 2018-11-13 10:00:35 -0800 | [diff] [blame] | 209 | if (isRegistered()) { |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 210 | ContextHubMsg messageToNanoApp = |
| 211 | ContextHubServiceUtil.createHidlContextHubMessage(mHostEndPointId, message); |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 212 | |
Arthur Ishiguro | 622ebcb | 2018-10-17 14:02:27 -0700 | [diff] [blame] | 213 | int contextHubId = mAttachedContextHubInfo.getId(); |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 214 | try { |
Arthur Ishiguro | 622ebcb | 2018-10-17 14:02:27 -0700 | [diff] [blame] | 215 | result = mContextHubProxy.sendMessageToHub(contextHubId, messageToNanoApp); |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 216 | } catch (RemoteException e) { |
| 217 | Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = " |
Arthur Ishiguro | 622ebcb | 2018-10-17 14:02:27 -0700 | [diff] [blame] | 218 | + contextHubId + ")", e); |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 219 | result = Result.UNKNOWN_FAILURE; |
| 220 | } |
| 221 | } else { |
| 222 | Log.e(TAG, "Failed to send message to nanoapp: client connection is closed"); |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 223 | result = Result.UNKNOWN_FAILURE; |
| 224 | } |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 225 | |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 226 | return ContextHubServiceUtil.toTransactionResult(result); |
| 227 | } |
| 228 | |
| 229 | /** |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 230 | * Closes the connection for this client with the service. |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 231 | * |
| 232 | * If the client has a PendingIntent registered, this method also unregisters it. |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 233 | */ |
| 234 | @Override |
| 235 | public void close() { |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 236 | synchronized (this) { |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 237 | mPendingIntentRequest.clear(); |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 238 | } |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 239 | onClientExit(); |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 240 | } |
| 241 | |
| 242 | /** |
| 243 | * Invoked when the underlying binder of this broker has died at the client process. |
| 244 | */ |
Arthur Ishiguro | 9f6284e | 2018-10-10 11:09:02 -0700 | [diff] [blame] | 245 | @Override |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 246 | public void binderDied() { |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 247 | onClientExit(); |
Arthur Ishiguro | 49e030f | 2017-11-16 11:54:42 -0800 | [diff] [blame] | 248 | } |
| 249 | |
| 250 | /** |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 251 | * @return the ID of the context hub this client is attached to |
| 252 | */ |
| 253 | /* package */ int getAttachedContextHubId() { |
Arthur Ishiguro | 622ebcb | 2018-10-17 14:02:27 -0700 | [diff] [blame] | 254 | return mAttachedContextHubInfo.getId(); |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 255 | } |
| 256 | |
| 257 | /** |
| 258 | * @return the host endpoint ID of this client |
| 259 | */ |
| 260 | /* package */ short getHostEndPointId() { |
| 261 | return mHostEndPointId; |
| 262 | } |
| 263 | |
| 264 | /** |
| 265 | * Sends a message to the client associated with this object. |
| 266 | * |
| 267 | * @param message the message that came from a nanoapp |
| 268 | */ |
| 269 | /* package */ void sendMessageToClient(NanoAppMessage message) { |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 270 | invokeCallback(callback -> callback.onMessageFromNanoApp(message)); |
Arthur Ishiguro | 9aa88e0 | 2018-10-16 10:26:57 -0700 | [diff] [blame] | 271 | |
| 272 | Supplier<Intent> supplier = |
| 273 | () -> createIntent(ContextHubManager.EVENT_NANOAPP_MESSAGE, message.getNanoAppId()) |
| 274 | .putExtra(ContextHubManager.EXTRA_MESSAGE, message); |
Arthur Ishiguro | bb8ca1a | 2018-12-06 13:49:40 -0800 | [diff] [blame] | 275 | sendPendingIntent(supplier, message.getNanoAppId()); |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 276 | } |
Arthur Ishiguro | 6d47c54 | 2017-11-17 15:49:07 -0800 | [diff] [blame] | 277 | |
| 278 | /** |
Arthur Ishiguro | 02ff50b | 2017-12-18 10:02:35 -0800 | [diff] [blame] | 279 | * Notifies the client of a nanoapp load event if the connection is open. |
Arthur Ishiguro | 0069f12 | 2017-11-20 15:07:14 -0800 | [diff] [blame] | 280 | * |
| 281 | * @param nanoAppId the ID of the nanoapp that was loaded. |
| 282 | */ |
| 283 | /* package */ void onNanoAppLoaded(long nanoAppId) { |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 284 | invokeCallback(callback -> callback.onNanoAppLoaded(nanoAppId)); |
Arthur Ishiguro | bb8ca1a | 2018-12-06 13:49:40 -0800 | [diff] [blame] | 285 | sendPendingIntent( |
| 286 | () -> createIntent(ContextHubManager.EVENT_NANOAPP_LOADED, nanoAppId), nanoAppId); |
Arthur Ishiguro | 0069f12 | 2017-11-20 15:07:14 -0800 | [diff] [blame] | 287 | } |
| 288 | |
| 289 | /** |
Arthur Ishiguro | 02ff50b | 2017-12-18 10:02:35 -0800 | [diff] [blame] | 290 | * Notifies the client of a nanoapp unload event if the connection is open. |
Arthur Ishiguro | 0069f12 | 2017-11-20 15:07:14 -0800 | [diff] [blame] | 291 | * |
| 292 | * @param nanoAppId the ID of the nanoapp that was unloaded. |
| 293 | */ |
| 294 | /* package */ void onNanoAppUnloaded(long nanoAppId) { |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 295 | invokeCallback(callback -> callback.onNanoAppUnloaded(nanoAppId)); |
Arthur Ishiguro | bb8ca1a | 2018-12-06 13:49:40 -0800 | [diff] [blame] | 296 | sendPendingIntent( |
| 297 | () -> createIntent(ContextHubManager.EVENT_NANOAPP_UNLOADED, nanoAppId), nanoAppId); |
Arthur Ishiguro | 0069f12 | 2017-11-20 15:07:14 -0800 | [diff] [blame] | 298 | } |
| 299 | |
| 300 | /** |
Arthur Ishiguro | 02ff50b | 2017-12-18 10:02:35 -0800 | [diff] [blame] | 301 | * Notifies the client of a hub reset event if the connection is open. |
Arthur Ishiguro | 6d47c54 | 2017-11-17 15:49:07 -0800 | [diff] [blame] | 302 | */ |
| 303 | /* package */ void onHubReset() { |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 304 | invokeCallback(callback -> callback.onHubReset()); |
Arthur Ishiguro | 9aa88e0 | 2018-10-16 10:26:57 -0700 | [diff] [blame] | 305 | sendPendingIntent(() -> createIntent(ContextHubManager.EVENT_HUB_RESET)); |
Arthur Ishiguro | 6d47c54 | 2017-11-17 15:49:07 -0800 | [diff] [blame] | 306 | } |
Arthur Ishiguro | 02ff50b | 2017-12-18 10:02:35 -0800 | [diff] [blame] | 307 | |
| 308 | /** |
| 309 | * Notifies the client of a nanoapp abort event if the connection is open. |
| 310 | * |
| 311 | * @param nanoAppId the ID of the nanoapp that aborted |
| 312 | * @param abortCode the nanoapp specific abort code |
| 313 | */ |
| 314 | /* package */ void onNanoAppAborted(long nanoAppId, int abortCode) { |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 315 | invokeCallback(callback -> callback.onNanoAppAborted(nanoAppId, abortCode)); |
Arthur Ishiguro | 9aa88e0 | 2018-10-16 10:26:57 -0700 | [diff] [blame] | 316 | |
| 317 | Supplier<Intent> supplier = |
| 318 | () -> createIntent(ContextHubManager.EVENT_NANOAPP_ABORTED, nanoAppId) |
| 319 | .putExtra(ContextHubManager.EXTRA_NANOAPP_ABORT_CODE, abortCode); |
Arthur Ishiguro | bb8ca1a | 2018-12-06 13:49:40 -0800 | [diff] [blame] | 320 | sendPendingIntent(supplier, nanoAppId); |
Arthur Ishiguro | 661f502 | 2018-10-10 12:17:29 -0700 | [diff] [blame] | 321 | } |
| 322 | |
| 323 | /** |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 324 | * @param intent the PendingIntent to compare to |
| 325 | * @param nanoAppId the ID of the nanoapp of the PendingIntent to compare to |
Arthur Ishiguro | d3464c7 | 2018-10-16 11:08:28 -0700 | [diff] [blame] | 326 | * @return true if the given PendingIntent is currently registered, false otherwise |
| 327 | */ |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 328 | /* package */ boolean hasPendingIntent(PendingIntent intent, long nanoAppId) { |
Arthur Ishiguro | d3464c7 | 2018-10-16 11:08:28 -0700 | [diff] [blame] | 329 | PendingIntent pendingIntent = null; |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 330 | long intentNanoAppId; |
Arthur Ishiguro | d3464c7 | 2018-10-16 11:08:28 -0700 | [diff] [blame] | 331 | synchronized (this) { |
| 332 | pendingIntent = mPendingIntentRequest.getPendingIntent(); |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 333 | intentNanoAppId = mPendingIntentRequest.getNanoAppId(); |
Arthur Ishiguro | d3464c7 | 2018-10-16 11:08:28 -0700 | [diff] [blame] | 334 | } |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 335 | return (pendingIntent != null) && pendingIntent.equals(intent) |
| 336 | && intentNanoAppId == nanoAppId; |
Arthur Ishiguro | d3464c7 | 2018-10-16 11:08:28 -0700 | [diff] [blame] | 337 | } |
| 338 | |
| 339 | /** |
Arthur Ishiguro | b549286 | 2018-11-13 10:51:15 -0800 | [diff] [blame] | 340 | * Attaches the death recipient to the callback interface object, if any. |
| 341 | * |
| 342 | * @throws RemoteException if the client process already died |
| 343 | */ |
| 344 | /* package */ void attachDeathRecipient() throws RemoteException { |
| 345 | if (mCallbackInterface != null) { |
| 346 | mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */); |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | /** |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 351 | * @return true if the client is a PendingIntent client that has been cancelled. |
| 352 | */ |
| 353 | /* package */ boolean isPendingIntentCancelled() { |
| 354 | return mIsPendingIntentCancelled.get(); |
| 355 | } |
| 356 | |
| 357 | /** |
Arthur Ishiguro | 661f502 | 2018-10-10 12:17:29 -0700 | [diff] [blame] | 358 | * Helper function to invoke a specified client callback, if the connection is open. |
| 359 | * |
| 360 | * @param consumer the consumer specifying the callback to invoke |
| 361 | */ |
Arthur Ishiguro | daeed3c | 2018-10-01 15:42:27 -0700 | [diff] [blame] | 362 | private synchronized void invokeCallback(CallbackConsumer consumer) { |
| 363 | if (mCallbackInterface != null) { |
Arthur Ishiguro | 02ff50b | 2017-12-18 10:02:35 -0800 | [diff] [blame] | 364 | try { |
Arthur Ishiguro | 661f502 | 2018-10-10 12:17:29 -0700 | [diff] [blame] | 365 | consumer.accept(mCallbackInterface); |
Arthur Ishiguro | 02ff50b | 2017-12-18 10:02:35 -0800 | [diff] [blame] | 366 | } catch (RemoteException e) { |
Arthur Ishiguro | 661f502 | 2018-10-10 12:17:29 -0700 | [diff] [blame] | 367 | Log.e(TAG, "RemoteException while invoking client callback (host endpoint ID = " |
| 368 | + mHostEndPointId + ")", e); |
Arthur Ishiguro | 02ff50b | 2017-12-18 10:02:35 -0800 | [diff] [blame] | 369 | } |
| 370 | } |
| 371 | } |
Arthur Ishiguro | 9aa88e0 | 2018-10-16 10:26:57 -0700 | [diff] [blame] | 372 | |
| 373 | /** |
| 374 | * Creates an Intent object containing the ContextHubManager.EXTRA_EVENT_TYPE extra field |
| 375 | * |
| 376 | * @param eventType the ContextHubManager.Event type describing the event |
| 377 | * @return the Intent object |
| 378 | */ |
| 379 | private Intent createIntent(int eventType) { |
| 380 | Intent intent = new Intent(); |
| 381 | intent.putExtra(ContextHubManager.EXTRA_EVENT_TYPE, eventType); |
| 382 | intent.putExtra(ContextHubManager.EXTRA_CONTEXT_HUB_INFO, mAttachedContextHubInfo); |
| 383 | return intent; |
| 384 | } |
| 385 | |
| 386 | /** |
| 387 | * Creates an Intent object containing the ContextHubManager.EXTRA_EVENT_TYPE and the |
| 388 | * ContextHubManager.EXTRA_NANOAPP_ID extra fields |
| 389 | * |
| 390 | * @param eventType the ContextHubManager.Event type describing the event |
| 391 | * @param nanoAppId the ID of the nanoapp this event is for |
| 392 | * @return the Intent object |
| 393 | */ |
| 394 | private Intent createIntent(int eventType, long nanoAppId) { |
| 395 | Intent intent = createIntent(eventType); |
| 396 | intent.putExtra(ContextHubManager.EXTRA_NANOAPP_ID, nanoAppId); |
| 397 | return intent; |
| 398 | } |
| 399 | |
| 400 | /** |
| 401 | * Sends an intent to any existing PendingIntent |
| 402 | * |
| 403 | * @param supplier method to create the extra Intent |
| 404 | */ |
| 405 | private synchronized void sendPendingIntent(Supplier<Intent> supplier) { |
| 406 | if (mPendingIntentRequest.hasPendingIntent()) { |
Arthur Ishiguro | bb8ca1a | 2018-12-06 13:49:40 -0800 | [diff] [blame] | 407 | doSendPendingIntent(mPendingIntentRequest.getPendingIntent(), supplier.get()); |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | /** |
| 412 | * Sends an intent to any existing PendingIntent |
| 413 | * |
| 414 | * @param supplier method to create the extra Intent |
| 415 | * @param nanoAppId the ID of the nanoapp which this event is for |
| 416 | */ |
| 417 | private synchronized void sendPendingIntent(Supplier<Intent> supplier, long nanoAppId) { |
| 418 | if (mPendingIntentRequest.hasPendingIntent() |
| 419 | && mPendingIntentRequest.getNanoAppId() == nanoAppId) { |
| 420 | doSendPendingIntent(mPendingIntentRequest.getPendingIntent(), supplier.get()); |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | /** |
| 425 | * Sends a PendingIntent with extra Intent data |
| 426 | * |
| 427 | * @param pendingIntent the PendingIntent |
| 428 | * @param intent the extra Intent data |
| 429 | */ |
| 430 | private void doSendPendingIntent(PendingIntent pendingIntent, Intent intent) { |
| 431 | try { |
Arthur Ishiguro | 0cf065e | 2020-01-22 17:05:26 +0000 | [diff] [blame] | 432 | String requiredPermission = mHasAccessContextHubPermission |
| 433 | ? Manifest.permission.ACCESS_CONTEXT_HUB |
| 434 | : Manifest.permission.LOCATION_HARDWARE; |
Arthur Ishiguro | bb8ca1a | 2018-12-06 13:49:40 -0800 | [diff] [blame] | 435 | pendingIntent.send( |
| 436 | mContext, 0 /* code */, intent, null /* onFinished */, null /* Handler */, |
Arthur Ishiguro | 0cf065e | 2020-01-22 17:05:26 +0000 | [diff] [blame] | 437 | requiredPermission, null /* options */); |
Arthur Ishiguro | bb8ca1a | 2018-12-06 13:49:40 -0800 | [diff] [blame] | 438 | } catch (PendingIntent.CanceledException e) { |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 439 | mIsPendingIntentCancelled.set(true); |
Arthur Ishiguro | bb8ca1a | 2018-12-06 13:49:40 -0800 | [diff] [blame] | 440 | // The PendingIntent is no longer valid |
| 441 | Log.w(TAG, "PendingIntent has been canceled, unregistering from client" |
| 442 | + " (host endpoint ID " + mHostEndPointId + ")"); |
| 443 | close(); |
Arthur Ishiguro | 9aa88e0 | 2018-10-16 10:26:57 -0700 | [diff] [blame] | 444 | } |
| 445 | } |
Arthur Ishiguro | a96fbfe | 2018-11-13 10:00:35 -0800 | [diff] [blame] | 446 | |
| 447 | /** |
| 448 | * @return true if the client is still registered with the service, false otherwise |
| 449 | */ |
| 450 | private synchronized boolean isRegistered() { |
| 451 | return mRegistered; |
| 452 | } |
Arthur Ishiguro | 82ed3af | 2018-11-09 13:20:30 -0800 | [diff] [blame] | 453 | |
| 454 | /** |
| 455 | * Invoked when a client exits either explicitly or by binder death. |
| 456 | */ |
| 457 | private synchronized void onClientExit() { |
| 458 | if (mCallbackInterface != null) { |
| 459 | mCallbackInterface.asBinder().unlinkToDeath(this, 0 /* flags */); |
| 460 | mCallbackInterface = null; |
| 461 | } |
| 462 | if (!mPendingIntentRequest.hasPendingIntent() && mRegistered) { |
| 463 | mClientManager.unregisterClient(mHostEndPointId); |
| 464 | mRegistered = false; |
| 465 | } |
| 466 | } |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 467 | |
Mike Ma | ac0cae7 | 2020-01-15 17:26:24 -0800 | [diff] [blame] | 468 | /** |
| 469 | * Dump debugging info as ClientBrokerProto |
| 470 | * |
| 471 | * If the output belongs to a sub message, the caller is responsible for wrapping this function |
| 472 | * between {@link ProtoOutputStream#start(long)} and {@link ProtoOutputStream#end(long)}. |
| 473 | * |
| 474 | * @param proto the ProtoOutputStream to write to |
| 475 | */ |
| 476 | void dump(ProtoOutputStream proto) { |
| 477 | proto.write(ClientBrokerProto.ENDPOINT_ID, getHostEndPointId()); |
| 478 | proto.write(ClientBrokerProto.ATTACHED_CONTEXT_HUB_ID, getAttachedContextHubId()); |
| 479 | proto.write(ClientBrokerProto.PACKAGE, mPackage); |
| 480 | if (mPendingIntentRequest.isValid()) { |
| 481 | proto.write(ClientBrokerProto.PENDING_INTENT_REQUEST_VALID, true); |
| 482 | proto.write(ClientBrokerProto.NANO_APP_ID, mPendingIntentRequest.getNanoAppId()); |
| 483 | } |
| 484 | proto.write(ClientBrokerProto.HAS_PENDING_INTENT, mPendingIntentRequest.hasPendingIntent()); |
| 485 | proto.write(ClientBrokerProto.PENDING_INTENT_CANCELLED, isPendingIntentCancelled()); |
| 486 | proto.write(ClientBrokerProto.REGISTERED, mRegistered); |
| 487 | |
| 488 | } |
| 489 | |
Arthur Ishiguro | 672d8a7 | 2019-12-02 15:16:08 -0800 | [diff] [blame] | 490 | @Override |
| 491 | public String toString() { |
| 492 | String out = "[ContextHubClient "; |
| 493 | out += "endpointID: " + getHostEndPointId() + ", "; |
| 494 | out += "contextHub: " + getAttachedContextHubId() + ", "; |
| 495 | if (mPendingIntentRequest.isValid()) { |
| 496 | out += "intentCreatorPackage: " + mPackage + ", "; |
| 497 | out += "nanoAppId: 0x" + Long.toHexString(mPendingIntentRequest.getNanoAppId()); |
| 498 | } else { |
| 499 | out += "package: " + mPackage; |
| 500 | } |
| 501 | out += "]"; |
| 502 | |
| 503 | return out; |
| 504 | } |
Arthur Ishiguro | 4e39aa1 | 2017-11-14 14:59:08 -0800 | [diff] [blame] | 505 | } |