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