1 /* 2 * Copyright (C) 2018 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.server.wifi.util; 18 19 import android.annotation.NonNull; 20 import android.os.Handler; 21 import android.os.IBinder; 22 import android.os.RemoteException; 23 import android.util.Log; 24 25 import com.android.internal.util.Preconditions; 26 27 import java.util.ArrayList; 28 import java.util.HashMap; 29 import java.util.List; 30 import java.util.Map; 31 32 /** 33 * Holds a list of external app-provided binder callback objects and tracks the death 34 * of the callback object. 35 * @param <T> Callback object type. 36 */ 37 public class ExternalCallbackTracker<T> { 38 private static final String TAG = "WifiExternalCallbackTracker"; 39 40 /* Limit on number of registered callbacks to track and prevent potential memory leak */ 41 private static final int NUM_CALLBACKS_WARN_LIMIT = 10; 42 private static final int NUM_CALLBACKS_WTF_LIMIT = 20; 43 44 /** 45 * Container for storing info about each external callback and tracks it's death. 46 */ 47 private static class ExternalCallbackHolder<T> implements IBinder.DeathRecipient { 48 private final IBinder mBinder; 49 private final T mCallbackObject; 50 private final DeathCallback mDeathCallback; 51 52 /** 53 * Callback to be invoked on death of the app hosting the binder. 54 */ 55 public interface DeathCallback { 56 /** 57 * Called when the corresponding app has died. 58 */ onDeath()59 void onDeath(); 60 } 61 ExternalCallbackHolder(@onNull IBinder binder, @NonNull T callbackObject, @NonNull DeathCallback deathCallback)62 private ExternalCallbackHolder(@NonNull IBinder binder, @NonNull T callbackObject, 63 @NonNull DeathCallback deathCallback) { 64 mBinder = Preconditions.checkNotNull(binder); 65 mCallbackObject = Preconditions.checkNotNull(callbackObject); 66 mDeathCallback = Preconditions.checkNotNull(deathCallback); 67 } 68 69 /** 70 * Static method to create a new {@link ExternalCallbackHolder} object and register for 71 * death notification of the associated binder. 72 * @return an instance of {@link ExternalCallbackHolder} if there are no failures, otherwise 73 * null. 74 */ createAndLinkToDeath( @onNull IBinder binder, @NonNull T callbackObject, @NonNull DeathCallback deathCallback)75 public static <T> ExternalCallbackHolder createAndLinkToDeath( 76 @NonNull IBinder binder, @NonNull T callbackObject, 77 @NonNull DeathCallback deathCallback) { 78 ExternalCallbackHolder<T> externalCallback = 79 new ExternalCallbackHolder<T>(binder, callbackObject, deathCallback); 80 try { 81 binder.linkToDeath(externalCallback, 0); 82 } catch (RemoteException e) { 83 Log.e(TAG, "Error on linkToDeath - " + e); 84 return null; 85 } 86 return externalCallback; 87 } 88 89 /** 90 * Unlinks this object from binder death. 91 */ reset()92 public void reset() { 93 mBinder.unlinkToDeath(this, 0); 94 } 95 96 /** 97 * Retrieve the callback object. 98 */ getCallback()99 public T getCallback() { 100 return mCallbackObject; 101 } 102 103 /** 104 * App hosting the binder has died. 105 */ 106 @Override binderDied()107 public void binderDied() { 108 mDeathCallback.onDeath(); 109 Log.d(TAG, "Binder died " + mBinder); 110 } 111 } 112 113 private final Map<Integer, ExternalCallbackHolder<T>> mCallbacks; 114 private final Handler mHandler; 115 ExternalCallbackTracker(Handler handler)116 public ExternalCallbackTracker(Handler handler) { 117 mHandler = handler; 118 mCallbacks = new HashMap<>(); 119 } 120 121 /** 122 * Add a callback object to tracker. 123 * @return true on success, false on failure. 124 */ add(@onNull IBinder binder, @NonNull T callbackObject, int callbackIdentifier)125 public boolean add(@NonNull IBinder binder, @NonNull T callbackObject, int callbackIdentifier) { 126 ExternalCallbackHolder<T> externalCallback = ExternalCallbackHolder.createAndLinkToDeath( 127 binder, callbackObject, () -> { 128 mHandler.post(() -> { 129 Log.d(TAG, "Remove external callback on death " + callbackIdentifier); 130 remove(callbackIdentifier); 131 }); 132 }); 133 if (externalCallback == null) return false; 134 if (mCallbacks.containsKey(callbackIdentifier) && remove(callbackIdentifier)) { 135 Log.d(TAG, "Replacing callback " + callbackIdentifier); 136 } 137 mCallbacks.put(callbackIdentifier, externalCallback); 138 if (mCallbacks.size() > NUM_CALLBACKS_WTF_LIMIT) { 139 Log.wtf(TAG, "Too many callbacks: " + mCallbacks.size()); 140 } else if (mCallbacks.size() > NUM_CALLBACKS_WARN_LIMIT) { 141 Log.w(TAG, "Too many callbacks: " + mCallbacks.size()); 142 } 143 return true; 144 } 145 146 /** 147 * Remove a callback object to tracker. 148 * @return true on success, false on failure. 149 */ remove(int callbackIdentifier)150 public boolean remove(int callbackIdentifier) { 151 ExternalCallbackHolder<T> externalCallback = mCallbacks.remove(callbackIdentifier); 152 if (externalCallback == null) { 153 Log.w(TAG, "Unknown external callback " + callbackIdentifier); 154 return false; 155 } 156 externalCallback.reset(); 157 return true; 158 } 159 160 /** 161 * Retrieve all the callback objects in the tracker. 162 */ getCallbacks()163 public List<T> getCallbacks() { 164 List<T> callbacks = new ArrayList<>(); 165 for (ExternalCallbackHolder<T> externalCallback : mCallbacks.values()) { 166 callbacks.add(externalCallback.getCallback()); 167 } 168 return callbacks; 169 } 170 171 /** 172 * Retrieve the number of callback objects in the tracker. 173 */ getNumCallbacks()174 public int getNumCallbacks() { 175 return mCallbacks.size(); 176 } 177 178 /** 179 * Remove all callbacks registered. 180 */ clear()181 public void clear() { 182 for (ExternalCallbackHolder<T> externalCallback : mCallbacks.values()) { 183 externalCallback.reset(); 184 } 185 mCallbacks.clear(); 186 } 187 } 188