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.server.wifi.p2p; 18 19 import android.annotation.Nullable; 20 import android.net.MacAddress; 21 import android.os.IBinder; 22 import android.os.Message; 23 import android.util.Log; 24 import android.util.Pair; 25 26 import java.util.HashMap; 27 import java.util.List; 28 import java.util.Map; 29 import java.util.Objects; 30 import java.util.stream.Collectors; 31 32 /** 33 * Manage external approvers for Wi-Fi Direct peers. 34 */ 35 public class ExternalApproverManager { 36 private static final String TAG = "ExternalApproverManager"; 37 38 Map<Pair<IBinder, MacAddress>, ApproverEntry> mApprovers = new HashMap<>(); 39 40 // Look-up table for device addresses. 41 Map<MacAddress, ApproverEntry> mApproverByAddress = new HashMap<>(); 42 43 private boolean mVerboseLoggingEnabled = false; 44 45 /** 46 * Store the approver. 47 * 48 * @param key The client binder. 49 * @param deviceAddress The peer device address. 50 * @param message The approver message which is used for the callback. 51 * @return The previous entry associated with the key & the peer, or null if there was 52 * no mapping for key, peer pair 53 */ put(@ullable IBinder key, @Nullable MacAddress deviceAddress, @Nullable Message message)54 public ApproverEntry put(@Nullable IBinder key, 55 @Nullable MacAddress deviceAddress, @Nullable Message message) { 56 if (null == key) return null; 57 if (null == deviceAddress) return null; 58 if (null == message) return null; 59 60 // Use look-up table to ensure that only one approver is bounded to a peer. 61 ApproverEntry existEntry = mApproverByAddress.get(deviceAddress); 62 if (null != existEntry) { 63 logd("Replace an existing approver: " + existEntry); 64 mApprovers.remove(new Pair<>(existEntry.getKey(), existEntry.getAddress())); 65 mApproverByAddress.remove(existEntry.getAddress()); 66 } 67 68 // Make a copy of message, or it might be modified externally. 69 ApproverEntry newEntry = new ApproverEntry( 70 key, deviceAddress, Message.obtain(message)); 71 mApprovers.put(new Pair(key, deviceAddress), newEntry); 72 mApproverByAddress.put(deviceAddress, newEntry); 73 logd("Add an approver: " + newEntry); 74 return existEntry; 75 } 76 77 /** Return approvers associated with a client. */ get(@ullable IBinder key)78 public List<ApproverEntry> get(@Nullable IBinder key) { 79 if (null == key) return null; 80 81 return mApprovers.entrySet().stream() 82 .filter(e -> e.getKey().first.equals(key)) 83 .map(Map.Entry::getValue) 84 .collect(Collectors.toList()); 85 } 86 87 /** Return the approver associated with a peer. */ get(@ullable MacAddress deviceAddress)88 public ApproverEntry get(@Nullable MacAddress deviceAddress) { 89 if (null == deviceAddress) return null; 90 return mApproverByAddress.get(deviceAddress); 91 } 92 93 /** Return the approver associated with a client and a peer. */ get(@ullable IBinder key, @Nullable MacAddress deviceAddress)94 public ApproverEntry get(@Nullable IBinder key, 95 @Nullable MacAddress deviceAddress) { 96 if (null == key) return null; 97 if (null == deviceAddress) return null; 98 return mApprovers.get(new Pair<>(key, deviceAddress)); 99 } 100 101 /** Remove the approver associated with a peer. */ remove(@ullable MacAddress deviceAddress)102 public ApproverEntry remove(@Nullable MacAddress deviceAddress) { 103 if (null == deviceAddress) return null; 104 ApproverEntry entry = mApproverByAddress.remove(deviceAddress); 105 if (null != entry) { 106 mApprovers.remove(new Pair<>(entry.getKey(), entry.getAddress())); 107 } 108 return entry; 109 } 110 111 /** Remove the approver associated with a client and a peer. */ remove(@ullable IBinder key, @Nullable MacAddress deviceAddress)112 public ApproverEntry remove(@Nullable IBinder key, 113 @Nullable MacAddress deviceAddress) { 114 if (null == key) return null; 115 if (null == deviceAddress) return null; 116 117 ApproverEntry entry = mApprovers.remove(new Pair<>(key, deviceAddress)); 118 if (null == entry) return null; 119 120 mApproverByAddress.remove(deviceAddress); 121 return entry; 122 } 123 124 /** Remove approvers associated with a client. */ removeAll(@ullable IBinder key)125 public void removeAll(@Nullable IBinder key) { 126 if (null == key) return; 127 128 List<ApproverEntry> entries = get(key); 129 entries.forEach(e -> { 130 remove(e.getKey(), e.getAddress()); 131 }); 132 } 133 134 /** Enable verbose logging. */ enableVerboseLogging(boolean verboseEnabled)135 public void enableVerboseLogging(boolean verboseEnabled) { 136 mVerboseLoggingEnabled = verboseEnabled; 137 } 138 logd(String s)139 private void logd(String s) { 140 if (!mVerboseLoggingEnabled) return; 141 Log.d(TAG, s, null); 142 } 143 144 /** The approver data. */ 145 public class ApproverEntry { 146 IBinder mIBinder; 147 MacAddress mDeviceAddress; 148 Message mMessage; 149 ApproverEntry()150 private ApproverEntry() { 151 } 152 ApproverEntry(IBinder key, MacAddress deviceAddress, Message message)153 public ApproverEntry(IBinder key, MacAddress deviceAddress, Message message) { 154 mIBinder = key; 155 mDeviceAddress = deviceAddress; 156 mMessage = message; 157 } 158 getKey()159 public IBinder getKey() { 160 return mIBinder; 161 } 162 getAddress()163 public MacAddress getAddress() { 164 return mDeviceAddress; 165 } 166 getMessage()167 public Message getMessage() { 168 return Message.obtain(mMessage); 169 } 170 171 @Override equals(@ullable Object o)172 public boolean equals(@Nullable Object o) { 173 if (this == o) return true; 174 if (o == null || getClass() != o.getClass()) return false; 175 176 ApproverEntry that = (ApproverEntry) o; 177 return Objects.equals(this.mIBinder, that.mIBinder) 178 && Objects.equals(this.mDeviceAddress, that.mDeviceAddress) 179 && Objects.equals(this.mMessage, that.mMessage); 180 } 181 182 @Override hashCode()183 public int hashCode() { 184 int _hash = 1; 185 _hash = 31 * _hash + Objects.hashCode(mIBinder); 186 _hash = 31 * _hash + Objects.hashCode(mDeviceAddress); 187 _hash = 31 * _hash + Objects.hashCode(mMessage); 188 return _hash; 189 } 190 191 @Override toString()192 public String toString() { 193 StringBuilder sb = new StringBuilder(); 194 sb.append("Approver {IBinder=").append(mIBinder.toString()) 195 .append(", Peer=").append(mDeviceAddress) 196 .append(", Message=").append(mMessage).append("}"); 197 return sb.toString(); 198 } 199 } 200 } 201