1 /* 2 * Copyright (C) 2022 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.car.occupantconnection; 18 19 import static android.car.Car.CAR_INTENT_ACTION_RECEIVER_SERVICE; 20 import static android.car.CarOccupantZoneManager.INVALID_USER_ID; 21 import static android.car.occupantconnection.CarOccupantConnectionManager.CONNECTION_ERROR_NONE; 22 import static android.car.occupantconnection.CarOccupantConnectionManager.CONNECTION_ERROR_NOT_READY; 23 import static android.car.occupantconnection.CarOccupantConnectionManager.CONNECTION_ERROR_PEER_APP_NOT_INSTALLED; 24 import static android.car.occupantconnection.CarOccupantConnectionManager.CONNECTION_ERROR_UNKNOWN; 25 26 import static com.android.car.CarServiceUtils.assertPermission; 27 import static com.android.car.CarServiceUtils.checkCalledByPackage; 28 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 29 30 import android.annotation.IntDef; 31 import android.annotation.Nullable; 32 import android.car.Car; 33 import android.car.CarOccupantZoneManager.OccupantZoneInfo; 34 import android.car.builtin.util.Slogf; 35 import android.car.occupantconnection.IBackendConnectionResponder; 36 import android.car.occupantconnection.IBackendReceiver; 37 import android.car.occupantconnection.ICarOccupantConnection; 38 import android.car.occupantconnection.IConnectionRequestCallback; 39 import android.car.occupantconnection.IPayloadCallback; 40 import android.car.occupantconnection.Payload; 41 import android.content.ComponentName; 42 import android.content.Context; 43 import android.content.Intent; 44 import android.content.ServiceConnection; 45 import android.content.pm.PackageInfo; 46 import android.os.Binder; 47 import android.os.IBinder; 48 import android.os.RemoteException; 49 import android.os.UserHandle; 50 import android.util.ArrayMap; 51 import android.util.ArraySet; 52 53 import com.android.car.CarOccupantZoneService; 54 import com.android.car.CarServiceBase; 55 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 56 import com.android.car.internal.util.BinderKeyValueContainer; 57 import com.android.car.internal.util.BinderKeyValueContainer.BinderDeathCallback; 58 import com.android.car.internal.util.IndentingPrintWriter; 59 import com.android.internal.annotations.GuardedBy; 60 import com.android.internal.annotations.VisibleForTesting; 61 62 import java.lang.annotation.Retention; 63 import java.lang.annotation.RetentionPolicy; 64 import java.util.Set; 65 66 /** 67 * Service to implement API defined in 68 * {@link android.car.occupantconnection.CarOccupantConnectionManager}. 69 */ 70 public class CarOccupantConnectionService extends ICarOccupantConnection.Stub implements 71 CarServiceBase { 72 73 private static final String TAG = CarOccupantConnectionService.class.getSimpleName(); 74 private static final String INDENTATION_2 = " "; 75 private static final String INDENTATION_4 = " "; 76 77 private static final int NOTIFY_ON_DISCONNECT = 1; 78 private static final int NOTIFY_ON_FAILED = 2; 79 80 @IntDef(flag = false, prefix = {"NOTIFY_ON_"}, value = { 81 NOTIFY_ON_DISCONNECT, 82 NOTIFY_ON_FAILED 83 }) 84 @Retention(RetentionPolicy.SOURCE) 85 @interface NotifyCallbackType { 86 } 87 88 private final Context mContext; 89 private final Object mLock = new Object(); 90 private final CarOccupantZoneService mOccupantZoneService; 91 private final CarRemoteDeviceService mRemoteDeviceService; 92 93 /** 94 * A set of receiver services that this service has requested to bind but has not connected 95 * yet. Once a receiver service is connected, it will be removed from this set and put into 96 * {@link #mConnectedReceiverServiceMap}. 97 */ 98 @GuardedBy("mLock") 99 private final ArraySet<ClientId> mConnectingReceiverServices; 100 101 /** 102 * A map of connected receiver services. The key is the clientId of the receiver service, 103 * while the value is to the binder of the receiver service. 104 */ 105 @GuardedBy("mLock") 106 private final BinderKeyValueContainer<ClientId, IBackendReceiver> 107 mConnectedReceiverServiceMap; 108 109 /** A map of receiver services to their ServiceConnections. */ 110 @GuardedBy("mLock") 111 private final ArrayMap<ClientId, ServiceConnection> mReceiverServiceConnectionMap; 112 113 /** 114 * A map of receiver endpoints to be registered when the {@link 115 * android.car.occupantconnection.AbstractReceiverService} is connected. The key is its ID, 116 * and the value is its IPayloadCallback. When a receiver endpoint is registered successfully, 117 * it will be removed from this map and added into {@link #mRegisteredReceiverEndpointMap}. 118 */ 119 @GuardedBy("mLock") 120 private final BinderKeyValueContainer<ReceiverEndpointId, IPayloadCallback> 121 mPreregisteredReceiverEndpointMap; 122 123 /** 124 * A map of receiver endpoints that have been registered into the {@link 125 * android.car.occupantconnection.AbstractReceiverService}. The key is its ID, 126 * and the value is its IPayloadCallback. 127 * <p> 128 * Once this service has registered the receiver endpoint into the receiver service, it still 129 * stores the receiver endpoint ID and callback in this map. 130 * It stores the ID to avoid registering duplicate IDs (see {@link 131 * #assertNoDuplicateReceiverEndpointLocked}) and decides when to unbind the receiver service. 132 * If the receiver client crashes, it needs to remove the stale ID (otherwise it will be in a 133 * broken state permanently). To do that, it stores the callback in 134 * this BinderKeyValueContainer, thus the stale ID will be removed automatically once the 135 * callback dies. 136 */ 137 @GuardedBy("mLock") 138 private final BinderKeyValueContainer<ReceiverEndpointId, IPayloadCallback> 139 mRegisteredReceiverEndpointMap; 140 141 /** 142 * A map of connection requests that have not received any response from the receiver app yet. 143 * The request was not responded because the {@link 144 * android.car.occupantconnection.AbstractReceiverService} in the receiver app was not bound, 145 * or was bound but didn't respond to the request yet. 146 * The key is its ID, and the value is its IConnectionRequestCallback. 147 * <p> 148 * When a connection request has been responded by the receiver, the request will be 149 * removed from this map; what's more, if the response is acceptation, the request 150 * will be added into {@link #mAcceptedConnectionRequestMap}. 151 */ 152 @GuardedBy("mLock") 153 private final BinderKeyValueContainer<ConnectionId, IConnectionRequestCallback> 154 mPendingConnectionRequestMap; 155 156 /** 157 * A map of accepted connection requests. The key is its ID, and the value is its 158 * IConnectionRequestCallback. 159 */ 160 @GuardedBy("mLock") 161 private final BinderKeyValueContainer<ConnectionId, IConnectionRequestCallback> 162 mAcceptedConnectionRequestMap; 163 164 /** A set of established connection records. */ 165 @GuardedBy("mLock") 166 private final ArraySet<ConnectionRecord> mEstablishedConnections; 167 168 private final BinderDeathCallback<ConnectionId> mConnectedSenderDeathCallback = 169 staleConnection -> { 170 Slogf.e(TAG, "The sender was connected before, but now it is dead %s", 171 staleConnection.senderClient); 172 synchronized (mLock) { 173 handleSenderDisconnectedLocked(staleConnection); 174 } 175 }; 176 177 private final BinderDeathCallback<ConnectionId> mPendingConnectedSenderDeathCallback = 178 connectionToCancel -> { 179 Slogf.e(TAG, "The sender requested a connection before, but now it is dead %s", 180 connectionToCancel.senderClient); 181 synchronized (mLock) { 182 handleConnectionCanceledLocked(connectionToCancel); 183 } 184 }; 185 186 /** 187 * A class to handle the connection to {@link 188 * android.car.occupantconnection.AbstractReceiverService} in the receiver app. 189 */ 190 private final class ReceiverServiceConnection implements ServiceConnection { 191 192 private final ClientId mReceiverClient; 193 private final IBackendConnectionResponder mResponder; 194 @Nullable 195 private IBackendReceiver mReceiverService; 196 ReceiverServiceConnection(ClientId receiverClient)197 private ReceiverServiceConnection(ClientId receiverClient) { 198 mReceiverClient = receiverClient; 199 mResponder = new IBackendConnectionResponder.Stub() { 200 @Override 201 public void acceptConnection(OccupantZoneInfo senderZone) { 202 ClientId senderClient = getClientIdInOccupantZone(senderZone, 203 receiverClient.packageName); 204 if (senderClient == null) { 205 // senderClient can't be null because it requested a connection, but let's 206 // be cautious. 207 return; 208 } 209 ConnectionId connectionId = new ConnectionId(senderClient, receiverClient); 210 synchronized (mLock) { 211 IConnectionRequestCallback callback = 212 extractRequestCallbackToNotifyLocked(senderZone, receiverClient); 213 if (callback == null) { 214 return; 215 } 216 if (mReceiverService == null) { 217 // mReceiverService can't be null because mResponder is registered 218 // after onServiceConnected() in invoked, where mReceiverService will 219 // be initialized to a non-null value. But let's be cautious. 220 Slogf.wtf(TAG, "The receiver service accepted the connection request" 221 + " but mReceiverService is null: " + mReceiverClient); 222 return; 223 } 224 try { 225 // Both the sender and receiver should be notified for connection 226 // success. 227 callback.onConnected(receiverClient.occupantZone); 228 mReceiverService.onConnected(senderZone); 229 230 mAcceptedConnectionRequestMap.put(connectionId, callback); 231 mEstablishedConnections.add(new ConnectionRecord( 232 receiverClient.packageName, 233 senderZone.zoneId, 234 receiverClient.occupantZone.zoneId)); 235 } catch (RemoteException e) { 236 Slogf.e(TAG, e, "Failed to notify connection success"); 237 } 238 } 239 } 240 241 @Override 242 public void rejectConnection(OccupantZoneInfo senderZone, int rejectionReason) { 243 synchronized (mLock) { 244 IConnectionRequestCallback callback = 245 extractRequestCallbackToNotifyLocked(senderZone, receiverClient); 246 if (callback == null) { 247 return; 248 } 249 try { 250 // Only the sender needs to be notified for connection rejection 251 // since the connection was rejected by the receiver. 252 callback.onFailed(receiverClient.occupantZone, rejectionReason); 253 } catch (RemoteException e) { 254 Slogf.e(TAG, e, "Failed to notify the sender for connection" 255 + " rejection"); 256 } 257 } 258 } 259 }; 260 } 261 262 @Override onServiceConnected(ComponentName name, IBinder service)263 public void onServiceConnected(ComponentName name, IBinder service) { 264 Slogf.v(TAG, "onServiceConnected " + service); 265 mReceiverService = IBackendReceiver.Stub.asInterface(service); 266 try { 267 mReceiverService.registerBackendConnectionResponder(mResponder); 268 } catch (RemoteException e) { 269 Slogf.e(TAG, e, "Failed to register IBackendConnectionResponder"); 270 } 271 272 synchronized (mLock) { 273 // Update receiver service maps. 274 mConnectedReceiverServiceMap.put(mReceiverClient, mReceiverService); 275 mConnectingReceiverServices.remove(mReceiverClient); 276 277 // Register cached callbacks into AbstractReceiverService, and update receiver 278 // endpoint maps. 279 registerPreregisteredReceiverEndpointsLocked(mReceiverService, mReceiverClient); 280 281 // If there are cached connection requests, notify the AbstractReceiverService now. 282 sendCachedConnectionRequestLocked(mReceiverService, mReceiverClient); 283 } 284 } 285 286 @Override onServiceDisconnected(ComponentName name)287 public void onServiceDisconnected(ComponentName name) { 288 Slogf.v(TAG, "onServiceDisconnected " + name); 289 mReceiverService = null; 290 synchronized (mLock) { 291 mConnectingReceiverServices.remove(mReceiverClient); 292 mConnectedReceiverServiceMap.remove(mReceiverClient); 293 mReceiverServiceConnectionMap.remove(mReceiverClient); 294 295 for (int i = mPreregisteredReceiverEndpointMap.size() - 1; i >= 0; i--) { 296 ReceiverEndpointId receiverEndpoint = 297 mPreregisteredReceiverEndpointMap.keyAt(i); 298 if (receiverEndpoint.clientId.equals(mReceiverClient)) { 299 mPreregisteredReceiverEndpointMap.removeAt(i); 300 } 301 } 302 303 for (int i = mRegisteredReceiverEndpointMap.size() - 1; i >= 0; i--) { 304 ReceiverEndpointId receiverEndpoint = 305 mRegisteredReceiverEndpointMap.keyAt(i); 306 if (receiverEndpoint.clientId.equals(mReceiverClient)) { 307 mRegisteredReceiverEndpointMap.removeAt(i); 308 } 309 } 310 311 notifyPeersOfReceiverServiceDisconnect(mPendingConnectionRequestMap, 312 mReceiverClient, NOTIFY_ON_FAILED); 313 notifyPeersOfReceiverServiceDisconnect(mAcceptedConnectionRequestMap, 314 mReceiverClient, NOTIFY_ON_DISCONNECT); 315 316 for (int i = mEstablishedConnections.size() - 1; i >= 0; i--) { 317 ConnectionRecord connectionRecord = mEstablishedConnections.valueAt(i); 318 if (connectionRecord.packageName.equals(mReceiverClient.packageName) 319 && connectionRecord.receiverZoneId 320 == mReceiverClient.occupantZone.zoneId) { 321 mEstablishedConnections.removeAt(i); 322 } 323 } 324 } 325 } 326 } 327 CarOccupantConnectionService(Context context, CarOccupantZoneService occupantZoneService, CarRemoteDeviceService remoteDeviceService)328 public CarOccupantConnectionService(Context context, CarOccupantZoneService occupantZoneService, 329 CarRemoteDeviceService remoteDeviceService) { 330 this(context, occupantZoneService, remoteDeviceService, 331 /* connectingReceiverServices= */ new ArraySet<>(), 332 /* connectedReceiverServiceMap= */ new BinderKeyValueContainer<>(), 333 /* receiverServiceConnectionMap= */ new ArrayMap<>(), 334 /* preregisteredReceiverEndpointMap= */ new BinderKeyValueContainer<>(), 335 /* registeredReceiverEndpointMap= */ new BinderKeyValueContainer<>(), 336 /* pendingConnectionRequestMap= */ new BinderKeyValueContainer<>(), 337 /* acceptedConnectionRequestMap= */ new BinderKeyValueContainer<>(), 338 /* establishConnections= */ new ArraySet<>()); 339 } 340 341 @VisibleForTesting CarOccupantConnectionService(Context context, CarOccupantZoneService occupantZoneService, CarRemoteDeviceService remoteDeviceService, ArraySet<ClientId> connectingReceiverServices, BinderKeyValueContainer<ClientId, IBackendReceiver> connectedReceiverServiceMap, ArrayMap<ClientId, ServiceConnection> receiverServiceConnectionMap, BinderKeyValueContainer<ReceiverEndpointId, IPayloadCallback> preregisteredReceiverEndpointMap, BinderKeyValueContainer<ReceiverEndpointId, IPayloadCallback> registeredReceiverEndpointMap, BinderKeyValueContainer<ConnectionId, IConnectionRequestCallback> pendingConnectionRequestMap, BinderKeyValueContainer<ConnectionId, IConnectionRequestCallback> acceptedConnectionRequestMap, ArraySet<ConnectionRecord> establishedConnections)342 CarOccupantConnectionService(Context context, 343 CarOccupantZoneService occupantZoneService, 344 CarRemoteDeviceService remoteDeviceService, 345 ArraySet<ClientId> connectingReceiverServices, 346 BinderKeyValueContainer<ClientId, IBackendReceiver> connectedReceiverServiceMap, 347 ArrayMap<ClientId, ServiceConnection> receiverServiceConnectionMap, 348 BinderKeyValueContainer<ReceiverEndpointId, IPayloadCallback> 349 preregisteredReceiverEndpointMap, 350 BinderKeyValueContainer<ReceiverEndpointId, IPayloadCallback> 351 registeredReceiverEndpointMap, 352 BinderKeyValueContainer<ConnectionId, IConnectionRequestCallback> 353 pendingConnectionRequestMap, 354 BinderKeyValueContainer<ConnectionId, IConnectionRequestCallback> 355 acceptedConnectionRequestMap, 356 ArraySet<ConnectionRecord> establishedConnections) { 357 mContext = context; 358 mOccupantZoneService = occupantZoneService; 359 mRemoteDeviceService = remoteDeviceService; 360 mConnectingReceiverServices = connectingReceiverServices; 361 mConnectedReceiverServiceMap = connectedReceiverServiceMap; 362 mReceiverServiceConnectionMap = receiverServiceConnectionMap; 363 mPreregisteredReceiverEndpointMap = preregisteredReceiverEndpointMap; 364 mRegisteredReceiverEndpointMap = registeredReceiverEndpointMap; 365 mPendingConnectionRequestMap = pendingConnectionRequestMap; 366 mAcceptedConnectionRequestMap = acceptedConnectionRequestMap; 367 mEstablishedConnections = establishedConnections; 368 } 369 370 @Override init()371 public void init() { 372 synchronized (mLock) { 373 mAcceptedConnectionRequestMap.setBinderDeathCallback(mConnectedSenderDeathCallback); 374 mPendingConnectionRequestMap.setBinderDeathCallback( 375 mPendingConnectedSenderDeathCallback); 376 } 377 } 378 379 @Override release()380 public void release() { 381 // TODO(b/257117236): implement this method. 382 } 383 384 @Override 385 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) 386 /** Run `adb shell dumpsys car_service --services CarOccupantConnectionService` to dump. */ dump(IndentingPrintWriter writer)387 public void dump(IndentingPrintWriter writer) { 388 writer.println("*CarOccupantConnectionService*"); 389 synchronized (mLock) { 390 writer.printf("%smConnectingReceiverServices:\n", INDENTATION_2); 391 for (int i = 0; i < mConnectingReceiverServices.size(); i++) { 392 writer.printf("%s%s\n", INDENTATION_4, mConnectingReceiverServices.valueAt(i)); 393 } 394 writer.printf("%smConnectedReceiverServiceMap:\n", INDENTATION_2); 395 for (int i = 0; i < mConnectedReceiverServiceMap.size(); i++) { 396 ClientId id = mConnectedReceiverServiceMap.keyAt(i); 397 IBackendReceiver service = mConnectedReceiverServiceMap.valueAt(i); 398 writer.printf("%s%s, receiver service:%s\n", INDENTATION_4, id, service); 399 } 400 writer.printf("%smReceiverServiceConnectionMap:\n", INDENTATION_2); 401 for (int i = 0; i < mReceiverServiceConnectionMap.size(); i++) { 402 ClientId id = mReceiverServiceConnectionMap.keyAt(i); 403 ServiceConnection connection = mReceiverServiceConnectionMap.valueAt(i); 404 writer.printf("%s%s, connection:%s\n", INDENTATION_4, id, connection); 405 } 406 writer.printf("%smPreregisteredReceiverEndpointMap:\n", INDENTATION_2); 407 for (int i = 0; i < mPreregisteredReceiverEndpointMap.size(); i++) { 408 ReceiverEndpointId id = mPreregisteredReceiverEndpointMap.keyAt(i); 409 IPayloadCallback callback = mPreregisteredReceiverEndpointMap.valueAt(i); 410 writer.printf("%s%s, callback:%s\n", INDENTATION_4, id, callback); 411 } 412 writer.printf("%smRegisteredReceiverEndpointMap:\n", INDENTATION_2); 413 for (int i = 0; i < mRegisteredReceiverEndpointMap.size(); i++) { 414 ReceiverEndpointId id = mRegisteredReceiverEndpointMap.keyAt(i); 415 IPayloadCallback callback = mRegisteredReceiverEndpointMap.valueAt(i); 416 writer.printf("%s%s, callback:%s\n", INDENTATION_4, id, callback); 417 } 418 writer.printf("%smPendingConnectionRequestMap:\n", INDENTATION_2); 419 for (int i = 0; i < mPendingConnectionRequestMap.size(); i++) { 420 ConnectionId id = mPendingConnectionRequestMap.keyAt(i); 421 IConnectionRequestCallback callback = mPendingConnectionRequestMap.valueAt(i); 422 writer.printf("%s%s, callback:%s\n", INDENTATION_4, id, callback); 423 } 424 writer.printf("%smAcceptedConnectionRequestMap:\n", INDENTATION_2); 425 for (int i = 0; i < mAcceptedConnectionRequestMap.size(); i++) { 426 ConnectionId id = mAcceptedConnectionRequestMap.keyAt(i); 427 IConnectionRequestCallback callback = mAcceptedConnectionRequestMap.valueAt(i); 428 writer.printf("%s%s, callback:%s\n", INDENTATION_4, id, callback); 429 } 430 writer.printf("%smEstablishConnections:\n", INDENTATION_2); 431 for (int i = 0; i < mEstablishedConnections.size(); i++) { 432 writer.printf("%s%s\n", INDENTATION_4, mEstablishedConnections.valueAt(i)); 433 } 434 } 435 } 436 437 @Override registerReceiver(String packageName, String receiverEndpointId, IPayloadCallback callback)438 public void registerReceiver(String packageName, String receiverEndpointId, 439 IPayloadCallback callback) { 440 assertPermission(mContext, Car.PERMISSION_MANAGE_OCCUPANT_CONNECTION); 441 checkCalledByPackage(mContext, packageName); 442 443 ClientId receiverClient = getCallingClientId(packageName); 444 ReceiverEndpointId receiverEndpoint = 445 new ReceiverEndpointId(receiverClient, receiverEndpointId); 446 synchronized (mLock) { 447 assertNoDuplicateReceiverEndpointLocked(receiverEndpoint); 448 // If the AbstractReceiverService of the receiver app is connected already, register 449 // this receiver into AbstractReceiverService now. 450 IBackendReceiver receiverService = mConnectedReceiverServiceMap.get(receiverClient); 451 if (receiverService != null) { 452 registerReceiverEndpointLocked(receiverService, receiverEndpoint, callback); 453 return; 454 } 455 456 // Otherwise, cache this receiver callback for now. The cached receiver callback(s) 457 // will be registered into the AbstractReceiverService once it is connected. 458 mPreregisteredReceiverEndpointMap.put(receiverEndpoint, callback); 459 460 // And bind to the AbstractReceiverService if was not bound yet. 461 maybeBindReceiverServiceLocked(receiverClient); 462 } 463 } 464 465 @Override unregisterReceiver(String packageName, String receiverEndpointId)466 public void unregisterReceiver(String packageName, String receiverEndpointId) { 467 assertPermission(mContext, Car.PERMISSION_MANAGE_OCCUPANT_CONNECTION); 468 checkCalledByPackage(mContext, packageName); 469 470 ClientId receiverClient = getCallingClientId(packageName); 471 ReceiverEndpointId receiverEndpoint = 472 new ReceiverEndpointId(receiverClient, receiverEndpointId); 473 IBackendReceiver receiverService; 474 synchronized (mLock) { 475 assertHasReceiverEndpointLocked(receiverEndpoint); 476 receiverService = mConnectedReceiverServiceMap.get(receiverClient); 477 if (receiverService == null) { 478 // This could happen when unregisterReceiver() is called immediately after 479 // registerReceiver(). In this case, the receiver service is not connected yet. 480 Slogf.d(TAG, "The receiver service in " + receiverClient + " is being bound"); 481 mPreregisteredReceiverEndpointMap.remove(receiverEndpoint); 482 maybeUnbindReceiverServiceLocked(receiverClient); 483 return; 484 } 485 try { 486 receiverService.unregisterReceiver(receiverEndpointId); 487 mRegisteredReceiverEndpointMap.remove(receiverEndpoint); 488 maybeUnbindReceiverServiceLocked(receiverClient); 489 } catch (RemoteException e) { 490 Slogf.e(TAG, e, "Failed the unregister the receiver %s", receiverEndpoint); 491 } 492 } 493 } 494 495 @Override requestConnection(String packageName, OccupantZoneInfo receiverZone, IConnectionRequestCallback callback)496 public void requestConnection(String packageName, OccupantZoneInfo receiverZone, 497 IConnectionRequestCallback callback) { 498 assertPermission(mContext, Car.PERMISSION_MANAGE_OCCUPANT_CONNECTION); 499 checkCalledByPackage(mContext, packageName); 500 501 int connectionError = CONNECTION_ERROR_NONE; 502 ClientId senderClient = getCallingClientId(packageName); 503 ClientId receiverClient = getClientIdInOccupantZone(receiverZone, packageName); 504 // Note: don't call mRemoteDeviceService.getEndpointPackageInfo() because it requires 505 // PERMISSION_MANAGE_REMOTE_DEVICE. 506 PackageInfo senderInfo = 507 mRemoteDeviceService.getPackageInfoAsUser(packageName, senderClient.userId); 508 if (senderInfo == null) { 509 // This should not happen, but let's be cautious. 510 Slogf.e(TAG, "Failed to get the PackageInfo of the sender %s", senderClient); 511 connectionError = CONNECTION_ERROR_UNKNOWN; 512 } else if (!mRemoteDeviceService.isConnectionReady(receiverZone)) { 513 Slogf.e(TAG, "%s is not ready for connection", receiverZone); 514 connectionError = CONNECTION_ERROR_NOT_READY; 515 } else { 516 PackageInfo receiverInfo = receiverClient == null 517 ? null 518 : mRemoteDeviceService.getPackageInfoAsUser(packageName, receiverClient.userId); 519 if (receiverInfo == null) { 520 Slogf.e(TAG, "Peer app %s is not installed in %s", packageName, receiverZone); 521 connectionError = CONNECTION_ERROR_PEER_APP_NOT_INSTALLED; 522 } 523 } 524 525 if (connectionError != CONNECTION_ERROR_NONE) { 526 try { 527 callback.onFailed(receiverZone, connectionError); 528 } catch (RemoteException e) { 529 Slogf.e(TAG, e, "Failed to notify the sender %s of connection failure %d", 530 senderClient, connectionError); 531 } 532 return; 533 } 534 535 ConnectionId connectionId = new ConnectionId(senderClient, receiverClient); 536 synchronized (mLock) { 537 assertNoDuplicateConnectionRequestLocked(connectionId); 538 539 // Save the callback in mPendingConnectionRequestMap. 540 // The requester will be notified when there is a response from the receiver app. 541 mPendingConnectionRequestMap.put(connectionId, callback); 542 543 // If the AbstractReceiverService of the receiver app is bound, notify it of the 544 // request now. 545 IBackendReceiver receiverService = mConnectedReceiverServiceMap.get(receiverClient); 546 if (receiverService != null) { 547 try { 548 receiverService.onConnectionInitiated(senderClient.occupantZone, 549 senderInfo.getLongVersionCode(), senderInfo.signingInfo); 550 } catch (RemoteException e) { 551 Slogf.e(TAG, e, "Failed to notify the receiver for connection request"); 552 } 553 return; 554 } 555 // Otherwise, bind to it, and notify the requester once it is bound. 556 maybeBindReceiverServiceLocked(receiverClient); 557 } 558 } 559 560 @Override cancelConnection(String packageName, OccupantZoneInfo receiverZone)561 public void cancelConnection(String packageName, OccupantZoneInfo receiverZone) { 562 assertPermission(mContext, Car.PERMISSION_MANAGE_OCCUPANT_CONNECTION); 563 checkCalledByPackage(mContext, packageName); 564 565 ClientId senderClient = getCallingClientId(packageName); 566 ClientId receiverClient = getClientIdInOccupantZone(receiverZone, packageName); 567 if (receiverClient == null) { 568 // receiverClient can't be null (because the sender requested a connection to it, and 569 // it didn't throw an exception), but let's be cautious. 570 return; 571 } 572 ConnectionId connectionToCancel = new ConnectionId(senderClient, receiverClient); 573 synchronized (mLock) { 574 assertHasPendingConnectionRequestLocked(connectionToCancel); 575 mPendingConnectionRequestMap.remove(connectionToCancel); 576 handleConnectionCanceledLocked(connectionToCancel); 577 } 578 } 579 580 @Override sendPayload(String packageName, OccupantZoneInfo receiverZone, Payload payload)581 public void sendPayload(String packageName, OccupantZoneInfo receiverZone, Payload payload) { 582 assertPermission(mContext, Car.PERMISSION_MANAGE_OCCUPANT_CONNECTION); 583 checkCalledByPackage(mContext, packageName); 584 585 ClientId senderClient = getCallingClientId(packageName); 586 ClientId receiverClient = getClientIdInOccupantZone(receiverZone, packageName); 587 IBackendReceiver receiverService; 588 synchronized (mLock) { 589 assertConnectedLocked(packageName, senderClient.occupantZone, receiverZone); 590 // receiverClient can't be null because the sender is connected to it now. 591 receiverService = mConnectedReceiverServiceMap.get(receiverClient); 592 } 593 if (receiverService == null) { 594 // receiverService can't be null since it is connected, but let's be cautious. 595 throw new IllegalStateException("The receiver service in " + receiverClient 596 + "is not bound yet"); 597 } 598 try { 599 receiverService.onPayloadReceived(senderClient.occupantZone, payload); 600 } catch (RemoteException e) { 601 throw new IllegalStateException("The receiver client is dead " + receiverClient, e); 602 } 603 } 604 605 @Override disconnect(String packageName, OccupantZoneInfo receiverZone)606 public void disconnect(String packageName, OccupantZoneInfo receiverZone) { 607 assertPermission(mContext, Car.PERMISSION_MANAGE_OCCUPANT_CONNECTION); 608 checkCalledByPackage(mContext, packageName); 609 610 ClientId senderClient = getCallingClientId(packageName); 611 ClientId receiverClient = getClientIdInOccupantZone(receiverZone, packageName); 612 synchronized (mLock) { 613 assertConnectedLocked(packageName, senderClient.occupantZone, receiverZone); 614 615 // Remove the connection callback. 616 // receiverClient can't be null because the sender is connected to it now. 617 ConnectionId staleConnection = new ConnectionId(senderClient, receiverClient); 618 mAcceptedConnectionRequestMap.remove(staleConnection); 619 620 handleSenderDisconnectedLocked(staleConnection); 621 } 622 } 623 624 @Override isConnected(String packageName, OccupantZoneInfo receiverZone)625 public boolean isConnected(String packageName, OccupantZoneInfo receiverZone) { 626 assertPermission(mContext, Car.PERMISSION_MANAGE_OCCUPANT_CONNECTION); 627 checkCalledByPackage(mContext, packageName); 628 629 UserHandle senderUserHandle = Binder.getCallingUserHandle(); 630 OccupantZoneInfo senderZone = mOccupantZoneService.getOccupantZoneForUser(senderUserHandle); 631 synchronized (mLock) { 632 return isConnectedLocked(packageName, senderZone, receiverZone); 633 } 634 } 635 636 @GuardedBy("mLock") registerPreregisteredReceiverEndpointsLocked(IBackendReceiver receiverService, ClientId receiverClient)637 private void registerPreregisteredReceiverEndpointsLocked(IBackendReceiver receiverService, 638 ClientId receiverClient) { 639 for (int i = mPreregisteredReceiverEndpointMap.size() - 1; i >= 0; i--) { 640 ReceiverEndpointId receiverEndpoint = mPreregisteredReceiverEndpointMap.keyAt(i); 641 if (!receiverClient.equals(receiverEndpoint.clientId)) { 642 // This endpoint belongs to another client, so skip it. 643 continue; 644 } 645 String receiverEndpointId = receiverEndpoint.endpointId; 646 IPayloadCallback callback = mPreregisteredReceiverEndpointMap.valueAt(i); 647 try { 648 receiverService.registerReceiver(receiverEndpointId, callback); 649 // Only update the maps after registration succeeded. This allows to retry. 650 mPreregisteredReceiverEndpointMap.removeAt(i); 651 mRegisteredReceiverEndpointMap.put(receiverEndpoint, callback); 652 } catch (RemoteException e) { 653 Slogf.e(TAG, e, "Failed to register receiver"); 654 } 655 } 656 } 657 658 @VisibleForTesting getCallingClientId(String packageName)659 ClientId getCallingClientId(String packageName) { 660 UserHandle callingUserHandle = Binder.getCallingUserHandle(); 661 int callingUserId = callingUserHandle.getIdentifier(); 662 OccupantZoneInfo occupantZone = 663 mOccupantZoneService.getOccupantZoneForUser(callingUserHandle); 664 // Note: the occupantZone is not null because the calling user must be a valid user. 665 return new ClientId(occupantZone, callingUserId, packageName); 666 } 667 668 @Nullable getClientIdInOccupantZone(OccupantZoneInfo occupantZone, String packageName)669 private ClientId getClientIdInOccupantZone(OccupantZoneInfo occupantZone, 670 String packageName) { 671 int userId = mOccupantZoneService.getUserForOccupant(occupantZone.zoneId); 672 if (userId == INVALID_USER_ID) { 673 Slogf.e(TAG, "The user in %s is not assigned yet", occupantZone); 674 return null; 675 } 676 return new ClientId(occupantZone, userId, packageName); 677 } 678 679 @GuardedBy("mLock") assertNoDuplicateReceiverEndpointLocked(ReceiverEndpointId receiverEndpoint)680 private void assertNoDuplicateReceiverEndpointLocked(ReceiverEndpointId receiverEndpoint) { 681 if (hasReceiverEndpointLocked(receiverEndpoint)) { 682 throw new IllegalStateException("The receiver endpoint was registered already: " 683 + receiverEndpoint); 684 } 685 } 686 687 @GuardedBy("mLock") assertHasReceiverEndpointLocked(ReceiverEndpointId receiverEndpoint)688 private void assertHasReceiverEndpointLocked(ReceiverEndpointId receiverEndpoint) { 689 if (!hasReceiverEndpointLocked(receiverEndpoint)) { 690 throw new IllegalStateException("The receiver endpoint was not registered before: " 691 + receiverEndpoint); 692 } 693 } 694 695 @GuardedBy("mLock") hasReceiverEndpointLocked(ReceiverEndpointId receiverEndpoint)696 private boolean hasReceiverEndpointLocked(ReceiverEndpointId receiverEndpoint) { 697 return mPreregisteredReceiverEndpointMap.containsKey(receiverEndpoint) 698 || mRegisteredReceiverEndpointMap.containsKey(receiverEndpoint); 699 } 700 701 @GuardedBy("mLock") registerReceiverEndpointLocked(IBackendReceiver receiverService, ReceiverEndpointId receiverEndpoint, IPayloadCallback callback)702 private void registerReceiverEndpointLocked(IBackendReceiver receiverService, 703 ReceiverEndpointId receiverEndpoint, 704 IPayloadCallback callback) { 705 try { 706 receiverService.registerReceiver(receiverEndpoint.endpointId, callback); 707 mRegisteredReceiverEndpointMap.put(receiverEndpoint, callback); 708 } catch (RemoteException e) { 709 Slogf.e(TAG, e, "Failed to register receiver"); 710 } 711 } 712 713 @GuardedBy("mLock") maybeBindReceiverServiceLocked(ClientId receiverClient)714 private void maybeBindReceiverServiceLocked(ClientId receiverClient) { 715 if (mConnectedReceiverServiceMap.containsKey(receiverClient)) { 716 Slogf.i(TAG, "Don't bind to the receiver service in %s because it's already bound", 717 receiverClient); 718 return; 719 } 720 if (mConnectingReceiverServices.contains(receiverClient)) { 721 Slogf.i(TAG, "Don't bind to the receiver service in %s because it's being bound", 722 receiverClient); 723 return; 724 } 725 bindReceiverServiceLocked(receiverClient); 726 mConnectingReceiverServices.add(receiverClient); 727 } 728 729 @GuardedBy("mLock") bindReceiverServiceLocked(ClientId receiverClient)730 private void bindReceiverServiceLocked(ClientId receiverClient) { 731 Intent intent = new Intent(CAR_INTENT_ACTION_RECEIVER_SERVICE); 732 intent.setPackage(receiverClient.packageName); 733 ReceiverServiceConnection connection = new ReceiverServiceConnection(receiverClient); 734 UserHandle userHandle = UserHandle.of(receiverClient.userId); 735 mContext.bindServiceAsUser(intent, connection, 736 Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, userHandle); 737 mReceiverServiceConnectionMap.put(receiverClient, connection); 738 } 739 740 /** 741 * Unbinds the receiver service in {@code receiverClient} if there is no 742 * preregistered/registered receiver endpoint in {@code receiverClient}, and no 743 * pending/established connection to {@code receiverClient}. 744 */ 745 @GuardedBy("mLock") maybeUnbindReceiverServiceLocked(ClientId receiverClient)746 private void maybeUnbindReceiverServiceLocked(ClientId receiverClient) { 747 for (int i = 0; i < mRegisteredReceiverEndpointMap.size(); i++) { 748 ReceiverEndpointId receiverEndpoint = mRegisteredReceiverEndpointMap.keyAt(i); 749 if (receiverEndpoint.clientId.equals(receiverClient)) { 750 Slogf.i(TAG, "Don't unbind the receiver service because it has a receiver" 751 + "endpoint registered: " + receiverEndpoint); 752 return; 753 } 754 } 755 for (int i = 0; i < mPreregisteredReceiverEndpointMap.size(); i++) { 756 ReceiverEndpointId receiverEndpoint = mPreregisteredReceiverEndpointMap.keyAt(i); 757 if (receiverEndpoint.clientId.equals(receiverClient)) { 758 Slogf.i(TAG, "Don't unbind the receiver service because it has a receiver" 759 + "endpoint pending registered " + receiverEndpoint); 760 return; 761 } 762 } 763 for (int i = 0; i < mAcceptedConnectionRequestMap.size(); i++) { 764 ConnectionId connectionId = mAcceptedConnectionRequestMap.keyAt(i); 765 if (connectionId.receiverClient.equals(receiverClient)) { 766 Slogf.i(TAG, "Don't unbind the receiver service because there is a connection" 767 + " to it:" + connectionId); 768 return; 769 } 770 } 771 for (int i = 0; i < mPendingConnectionRequestMap.size(); i++) { 772 ConnectionId connectionId = mPendingConnectionRequestMap.keyAt(i); 773 if (connectionId.receiverClient.equals(receiverClient)) { 774 Slogf.i(TAG, "Don't unbind because there is a sender endpoint connecting" 775 + "to it:" + connectionId); 776 return; 777 } 778 } 779 780 unbindReceiverServiceLocked(receiverClient); 781 mConnectingReceiverServices.remove(receiverClient); 782 mConnectedReceiverServiceMap.remove(receiverClient); 783 } 784 785 @GuardedBy("mLock") unbindReceiverServiceLocked(ClientId receiverClient)786 private void unbindReceiverServiceLocked(ClientId receiverClient) { 787 ServiceConnection connection = mReceiverServiceConnectionMap.get(receiverClient); 788 if (connection == null) { 789 Slogf.w(TAG, "Failed to unbind to the receiver service in " + receiverClient 790 + " because it was not bound"); 791 return; 792 } 793 mContext.unbindService(connection); 794 mReceiverServiceConnectionMap.remove(receiverClient); 795 } 796 797 @GuardedBy("mLock") assertNoDuplicateConnectionRequestLocked(ConnectionId connectionId)798 private void assertNoDuplicateConnectionRequestLocked(ConnectionId connectionId) { 799 if (mPendingConnectionRequestMap.containsKey(connectionId)) { 800 throw new IllegalStateException("The client " + connectionId.senderClient 801 + " already requested a connection to " + connectionId.receiverClient 802 + " and is waiting for response"); 803 } 804 if (mAcceptedConnectionRequestMap.containsKey(connectionId)) { 805 throw new IllegalStateException("The client " + connectionId.senderClient 806 + " already established a connection to " + connectionId.receiverClient); 807 } 808 } 809 810 @GuardedBy("mLock") assertHasPendingConnectionRequestLocked(ConnectionId connectionId)811 private void assertHasPendingConnectionRequestLocked(ConnectionId connectionId) { 812 if (!mPendingConnectionRequestMap.containsKey(connectionId)) { 813 throw new IllegalStateException("The client " + connectionId.senderClient 814 + " has no pending connection request to " + connectionId.receiverClient); 815 } 816 } 817 notifyPeersOfReceiverServiceDisconnect( BinderKeyValueContainer<ConnectionId, IConnectionRequestCallback> connectionRequestMap, ClientId receiverClient, @NotifyCallbackType int callbackType)818 private void notifyPeersOfReceiverServiceDisconnect( 819 BinderKeyValueContainer<ConnectionId, IConnectionRequestCallback> 820 connectionRequestMap, ClientId receiverClient, 821 @NotifyCallbackType int callbackType) { 822 for (int i = connectionRequestMap.size() - 1; i >= 0; i--) { 823 ConnectionId connectionId = connectionRequestMap.keyAt(i); 824 if (!connectionId.receiverClient.equals(receiverClient)) { 825 continue; 826 } 827 IConnectionRequestCallback callback = connectionRequestMap.valueAt(i); 828 try { 829 switch (callbackType) { 830 case NOTIFY_ON_DISCONNECT: 831 callback.onDisconnected(receiverClient.occupantZone); 832 break; 833 case NOTIFY_ON_FAILED: 834 callback.onFailed(receiverClient.occupantZone, CONNECTION_ERROR_UNKNOWN); 835 break; 836 default: 837 throw new IllegalArgumentException("Undefined NotifyCallbackType: " 838 + callbackType); 839 } 840 } catch (RemoteException e) { 841 Slogf.e(TAG, e, "Failed to notify the sender for connection failure"); 842 } 843 connectionRequestMap.removeAt(i); 844 } 845 } 846 847 /** 848 * Returns whether the sender client is connected to the receiver client. 849 */ 850 @GuardedBy("mLock") isConnectedLocked(String packageName, OccupantZoneInfo senderZone, OccupantZoneInfo receiverZone)851 private boolean isConnectedLocked(String packageName, OccupantZoneInfo senderZone, 852 OccupantZoneInfo receiverZone) { 853 ConnectionRecord expectedConnection = 854 new ConnectionRecord(packageName, senderZone.zoneId, receiverZone.zoneId); 855 return mEstablishedConnections.contains(expectedConnection); 856 } 857 858 @GuardedBy("mLock") assertConnectedLocked(String packageName, OccupantZoneInfo senderZone, OccupantZoneInfo receiverZone)859 private void assertConnectedLocked(String packageName, OccupantZoneInfo senderZone, 860 OccupantZoneInfo receiverZone) { 861 if (!isConnectedLocked(packageName, senderZone, receiverZone)) { 862 throw new IllegalStateException("The client " + packageName + " in " + senderZone 863 + " is not connected to " + receiverZone); 864 } 865 } 866 867 @GuardedBy("mLock") extractRequestCallbackToNotifyLocked( OccupantZoneInfo senderZone, ClientId receiverClient)868 private IConnectionRequestCallback extractRequestCallbackToNotifyLocked( 869 OccupantZoneInfo senderZone, ClientId receiverClient) { 870 ClientId senderClient = getClientIdInOccupantZone(senderZone, receiverClient.packageName); 871 if (senderClient == null) { 872 // senderClient can't be null because it requested a connection, but let's be cautious. 873 return null; 874 } 875 ConnectionId connectionId = new ConnectionId(senderClient, receiverClient); 876 IConnectionRequestCallback pendingCallback = mPendingConnectionRequestMap.get(connectionId); 877 if (pendingCallback == null) { 878 Slogf.e(TAG, "The connection requester no longer exists " + senderClient); 879 return null; 880 } 881 mPendingConnectionRequestMap.remove(connectionId); 882 return pendingCallback; 883 } 884 885 @GuardedBy("mLock") sendCachedConnectionRequestLocked(IBackendReceiver receiverService, ClientId receiverClient)886 private void sendCachedConnectionRequestLocked(IBackendReceiver receiverService, 887 ClientId receiverClient) { 888 Set<ClientId> notifiedSenderClients = new ArraySet<>(); 889 for (int i = mPendingConnectionRequestMap.size() - 1; i >= 0; i--) { 890 ConnectionId connectionId = mPendingConnectionRequestMap.keyAt(i); 891 // If there is a pending request to the receiver service and the receiver service has 892 // not been notified of the request before, notify the receiver service now. 893 if (connectionId.receiverClient.equals(receiverClient) 894 && !notifiedSenderClients.contains(connectionId.senderClient)) { 895 // Note: don't call mRemoteDeviceService.getEndpointPackageInfo() because 896 // sendCachedConnectionRequestLocked() is called on the main thread instead of the 897 // binder thread, so the calling UID check in 898 // mRemoteDeviceService.getEndpointPackageInfo() will fail. 899 PackageInfo senderInfo = mRemoteDeviceService.getPackageInfoAsUser( 900 connectionId.senderClient.packageName, 901 connectionId.senderClient.userId); 902 if (senderInfo == null) { 903 // This should not happen, but let's be cautious. 904 Slogf.e(TAG, "Failed to get the PackageInfo of the sender %s", 905 connectionId.senderClient); 906 IConnectionRequestCallback callback = mPendingConnectionRequestMap.valueAt(i); 907 try { 908 callback.onFailed(receiverClient.occupantZone, CONNECTION_ERROR_UNKNOWN); 909 } catch (RemoteException e) { 910 Slogf.e(TAG, e, "Failed to notify the sender %s of connection failure", 911 connectionId.senderClient); 912 } 913 return; 914 } 915 try { 916 receiverService.onConnectionInitiated(connectionId.senderClient.occupantZone, 917 senderInfo.getLongVersionCode(), senderInfo.signingInfo); 918 notifiedSenderClients.add(connectionId.senderClient); 919 } catch (RemoteException e) { 920 Slogf.e(TAG, e, "Failed to notify the receiver for connection request"); 921 } 922 } 923 } 924 } 925 926 /** 927 * This method is invoked when the sender was connected before but is disconnected now. 928 * For example, the sender calls {@link #disconnect}, or dies. 929 */ 930 @GuardedBy("mLock") handleSenderDisconnectedLocked(ConnectionId staleConnection)931 private void handleSenderDisconnectedLocked(ConnectionId staleConnection) { 932 // Remove the connection record. 933 ConnectionRecord staleRecord = new ConnectionRecord( 934 staleConnection.senderClient.packageName, 935 staleConnection.senderClient.occupantZone.zoneId, 936 staleConnection.receiverClient.occupantZone.zoneId); 937 mEstablishedConnections.remove(staleRecord); 938 939 // Notify the receiver service. 940 IBackendReceiver receiverService = 941 mConnectedReceiverServiceMap.get(staleConnection.receiverClient); 942 if (receiverService == null) { 943 // receiverService can't be null since it must be connected when this method is called, 944 // but let's be cautious. 945 Slogf.e(TAG, "The receiver service in %s is not bound yet", 946 staleConnection.receiverClient); 947 return; 948 } 949 try { 950 receiverService.onDisconnected(staleConnection.senderClient.occupantZone); 951 } catch (RemoteException e) { 952 // There is no need to propagate the Exception to the sender client because 953 // the connection was terminated successfully anyway. 954 Slogf.e(TAG, e, "Failed to notify the receiver service of disconnection! " 955 + "senderClient:%s, receiverClient:%s", staleConnection.senderClient, 956 staleConnection.receiverClient); 957 } 958 959 maybeUnbindReceiverServiceLocked(staleConnection.receiverClient); 960 } 961 962 /** 963 * This method is invoked when a pending connection is canceled. For example, the sender calls 964 * {@link #cancelConnection}, or dies. 965 */ 966 @GuardedBy("mLock") handleConnectionCanceledLocked(ConnectionId connectionToCancel)967 private void handleConnectionCanceledLocked(ConnectionId connectionToCancel) { 968 IBackendReceiver receiverService = 969 mConnectedReceiverServiceMap.get(connectionToCancel.receiverClient); 970 // If the AbstractReceiverService of the receiver app is bound, notify it of the 971 // cancellation now. 972 if (receiverService != null) { 973 try { 974 receiverService.onConnectionCanceled(connectionToCancel.senderClient.occupantZone); 975 } catch (RemoteException e) { 976 // There is no need to propagate the Exception to the sender client because 977 // the connection was canceled successfully anyway. 978 Slogf.e(TAG, e, "Failed to notify the receiver service of connection request" 979 + " cancellation! senderClient:%s, receiverClient:%s", 980 connectionToCancel.senderClient, connectionToCancel.receiverClient); 981 } 982 } 983 // The receiverService may be bound already, or being bound. In either case, it needs to be 984 // unbound if it is not needed any more. 985 maybeUnbindReceiverServiceLocked(connectionToCancel.receiverClient); 986 } 987 } 988