1 /*
2  * Copyright (C) 2019 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.internal.os;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.IBinder;
22 import android.os.IBinder.DeathRecipient;
23 import android.os.IInterface;
24 import android.os.RemoteException;
25 import android.util.ArrayMap;
26 import android.util.ArraySet;
27 import android.util.IndentingPrintWriter;
28 
29 import com.android.internal.annotations.GuardedBy;
30 import com.android.internal.annotations.VisibleForTesting;
31 
32 /**
33  * Multiplexes multiple binder death recipients on the same binder objects, so that at the native
34  * level, we only need to keep track of one death recipient reference. This will help reduce the
35  * number of JNI strong references.
36  *
37  * test with: atest FrameworksCoreTests:BinderDeathDispatcherTest
38  */
39 @android.ravenwood.annotation.RavenwoodKeepWholeClass
40 public class BinderDeathDispatcher<T extends IInterface> {
41     private static final String TAG = "BinderDeathDispatcher";
42 
43     private final Object mLock = new Object();
44 
45     @GuardedBy("mLock")
46     private final ArrayMap<IBinder, RecipientsInfo> mTargets = new ArrayMap<>();
47 
48     @VisibleForTesting
49     class RecipientsInfo implements DeathRecipient {
50         final IBinder mTarget;
51 
52         /**
53          * Recipient list. If it's null, {@link #mTarget} has already died, but in that case
54          * this RecipientsInfo instance is removed from {@link #mTargets}.
55          */
56         @GuardedBy("mLock")
57         @Nullable
58         ArraySet<DeathRecipient> mRecipients = new ArraySet<>();
59 
RecipientsInfo(IBinder target)60         private RecipientsInfo(IBinder target) {
61             mTarget = target;
62         }
63 
64         @Override
binderDied()65         public void binderDied() {
66         }
67 
68         @Override
binderDied(IBinder who)69         public void binderDied(IBinder who) {
70             final ArraySet<DeathRecipient> copy;
71             synchronized (mLock) {
72                 copy = mRecipients;
73                 mRecipients = null;
74 
75                 // Also remove from the targets.
76                 mTargets.remove(mTarget);
77             }
78             if (copy == null) {
79                 return;
80             }
81             // Let's call it without holding the lock.
82             final int size = copy.size();
83             for (int i = 0; i < size; i++) {
84                 copy.valueAt(i).binderDied(who);
85             }
86         }
87     }
88 
89     /**
90      * Add a {@code recipient} to the death recipient list on {@code target}.
91      *
92      * @return # of recipients in the recipient list, including {@code recipient}. Or, -1
93      * if {@code target} is already dead, in which case recipient's
94      * {@link DeathRecipient#binderDied} won't be called.
95      */
linkToDeath(@onNull T target, @NonNull DeathRecipient recipient)96     public int linkToDeath(@NonNull T target, @NonNull DeathRecipient recipient) {
97         final IBinder ib = target.asBinder();
98         synchronized (mLock) {
99             RecipientsInfo info = mTargets.get(ib);
100             if (info == null) {
101                 info = new RecipientsInfo(ib);
102 
103                 // First recipient; need to link to death.
104                 try {
105                     ib.linkToDeath(info, 0);
106                 } catch (RemoteException e) {
107                     return -1; // Already dead.
108                 }
109                 mTargets.put(ib, info);
110             }
111             info.mRecipients.add(recipient);
112             return info.mRecipients.size();
113         }
114     }
115 
unlinkToDeath(@onNull T target, @NonNull DeathRecipient recipient)116     public void unlinkToDeath(@NonNull T target, @NonNull DeathRecipient recipient) {
117         final IBinder ib = target.asBinder();
118 
119         synchronized (mLock) {
120             final RecipientsInfo info = mTargets.get(ib);
121             if (info == null) {
122                 return;
123             }
124             if (info.mRecipients.remove(recipient) && info.mRecipients.size() == 0) {
125                 info.mTarget.unlinkToDeath(info, 0);
126                 mTargets.remove(info.mTarget);
127             }
128         }
129     }
130 
131     /** Dump stats useful for debugging. Can be used from dump() methods of client services. */
dump(IndentingPrintWriter pw)132     public void dump(IndentingPrintWriter pw) {
133         synchronized (mLock) {
134             pw.print("# of watched binders: ");
135             pw.println(mTargets.size());
136 
137             pw.print("# of death recipients: ");
138             int n = 0;
139             for (RecipientsInfo info : mTargets.values()) {
140                 n += info.mRecipients.size();
141             }
142             pw.println(n);
143         }
144     }
145 
146     @VisibleForTesting
getTargetsForTest()147     public ArrayMap<IBinder, RecipientsInfo> getTargetsForTest() {
148         return mTargets;
149     }
150 }
151