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 public class BinderDeathDispatcher<T extends IInterface> {
40     private static final String TAG = "BinderDeathDispatcher";
41 
42     private final Object mLock = new Object();
43 
44     @GuardedBy("mLock")
45     private final ArrayMap<IBinder, RecipientsInfo> mTargets = new ArrayMap<>();
46 
47     @VisibleForTesting
48     class RecipientsInfo implements DeathRecipient {
49         final IBinder mTarget;
50 
51         /**
52          * Recipient list. If it's null, {@link #mTarget} has already died, but in that case
53          * this RecipientsInfo instance is removed from {@link #mTargets}.
54          */
55         @GuardedBy("mLock")
56         @Nullable
57         ArraySet<DeathRecipient> mRecipients = new ArraySet<>();
58 
RecipientsInfo(IBinder target)59         private RecipientsInfo(IBinder target) {
60             mTarget = target;
61         }
62 
63         @Override
binderDied()64         public void binderDied() {
65         }
66 
67         @Override
binderDied(IBinder who)68         public void binderDied(IBinder who) {
69             final ArraySet<DeathRecipient> copy;
70             synchronized (mLock) {
71                 copy = mRecipients;
72                 mRecipients = null;
73 
74                 // Also remove from the targets.
75                 mTargets.remove(mTarget);
76             }
77             if (copy == null) {
78                 return;
79             }
80             // Let's call it without holding the lock.
81             final int size = copy.size();
82             for (int i = 0; i < size; i++) {
83                 copy.valueAt(i).binderDied(who);
84             }
85         }
86     }
87 
88     /**
89      * Add a {@code recipient} to the death recipient list on {@code target}.
90      *
91      * @return # of recipients in the recipient list, including {@code recipient}. Or, -1
92      * if {@code target} is already dead, in which case recipient's
93      * {@link DeathRecipient#binderDied} won't be called.
94      */
linkToDeath(@onNull T target, @NonNull DeathRecipient recipient)95     public int linkToDeath(@NonNull T target, @NonNull DeathRecipient recipient) {
96         final IBinder ib = target.asBinder();
97         synchronized (mLock) {
98             RecipientsInfo info = mTargets.get(ib);
99             if (info == null) {
100                 info = new RecipientsInfo(ib);
101 
102                 // First recipient; need to link to death.
103                 try {
104                     ib.linkToDeath(info, 0);
105                 } catch (RemoteException e) {
106                     return -1; // Already dead.
107                 }
108                 mTargets.put(ib, info);
109             }
110             info.mRecipients.add(recipient);
111             return info.mRecipients.size();
112         }
113     }
114 
unlinkToDeath(@onNull T target, @NonNull DeathRecipient recipient)115     public void unlinkToDeath(@NonNull T target, @NonNull DeathRecipient recipient) {
116         final IBinder ib = target.asBinder();
117 
118         synchronized (mLock) {
119             final RecipientsInfo info = mTargets.get(ib);
120             if (info == null) {
121                 return;
122             }
123             if (info.mRecipients.remove(recipient) && info.mRecipients.size() == 0) {
124                 info.mTarget.unlinkToDeath(info, 0);
125                 mTargets.remove(info.mTarget);
126             }
127         }
128     }
129 
130     /** Dump stats useful for debugging. Can be used from dump() methods of client services. */
dump(IndentingPrintWriter pw)131     public void dump(IndentingPrintWriter pw) {
132         synchronized (mLock) {
133             pw.print("# of watched binders: ");
134             pw.println(mTargets.size());
135 
136             pw.print("# of death recipients: ");
137             int n = 0;
138             for (RecipientsInfo info : mTargets.values()) {
139                 n += info.mRecipients.size();
140             }
141             pw.println(n);
142         }
143     }
144 
145     @VisibleForTesting
getTargetsForTest()146     public ArrayMap<IBinder, RecipientsInfo> getTargetsForTest() {
147         return mTargets;
148     }
149 }
150