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