• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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