1 /* 2 * Copyright (C) 2020 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; 18 19 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_PRIMARY; 20 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SCAN_ONLY; 21 import static com.android.server.wifi.ActiveModeManager.ROLE_CLIENT_SECONDARY_TRANSIENT; 22 23 import android.annotation.NonNull; 24 import android.content.Context; 25 import android.net.NetworkInfo; 26 import android.util.ArrayMap; 27 28 import androidx.annotation.Nullable; 29 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.List; 33 import java.util.Map; 34 35 /** 36 * Used to buffer public broadcasts when multiple concurrent client interfaces are active to 37 * preserve legacy behavior expected by apps when there is a single client interface active. 38 */ 39 public class ClientModeManagerBroadcastQueue { 40 41 private static final String TAG = "WifiBroadcastQueue"; 42 43 private final ActiveModeWarden mActiveModeWarden; 44 private final Context mContext; 45 /** List of buffered broadcasts, per-ClientModeManager. */ 46 private final Map<ClientModeManager, List<QueuedBroadcast>> mBufferedBroadcasts = 47 new ArrayMap<>(); 48 49 /** Lambda representing a broadcast to be sent. */ 50 public interface QueuedBroadcast { 51 /** Send the broadcast using one of the many different Context#send* implementations. */ send()52 void send(); 53 } 54 55 private boolean mVerboseLoggingEnabled = false; 56 ClientModeManagerBroadcastQueue(@onNull ActiveModeWarden activeModeWarden, @NonNull Context context)57 public ClientModeManagerBroadcastQueue(@NonNull ActiveModeWarden activeModeWarden, 58 @NonNull Context context) { 59 mActiveModeWarden = activeModeWarden; 60 mContext = context; 61 62 mActiveModeWarden.registerModeChangeCallback(new ModeChangeCallback()); 63 mActiveModeWarden.registerPrimaryClientModeManagerChangedCallback( 64 new PrimaryClientModeManagerChangedCallback()); 65 } 66 setVerboseLoggingEnabled(boolean verboseLoggingEnabled)67 public void setVerboseLoggingEnabled(boolean verboseLoggingEnabled) { 68 mVerboseLoggingEnabled = verboseLoggingEnabled; 69 } 70 71 /** 72 * If the ClientModeManager is primary or scan only, the broadcast will be sent immediately. 73 * Otherwise, the broadcast will be queued, and sent out if and when the ClientModeManager 74 * becomes primary. 75 */ queueOrSendBroadcast( @onNull ClientModeManager manager, @NonNull QueuedBroadcast broadcast)76 public void queueOrSendBroadcast( 77 @NonNull ClientModeManager manager, 78 @NonNull QueuedBroadcast broadcast) { 79 80 if (manager.getRole() == ROLE_CLIENT_PRIMARY 81 || manager.getRole() == ROLE_CLIENT_SCAN_ONLY) { 82 // Primary or scan only, send existing queued broadcasts and send the new broadcast 83 // immediately. Assume that queue is empty for this manager (flushed when it originally 84 // became primary). 85 // TODO: b/192612399 - look into the race issue causing the ClientModeManager to be 86 // already in ROLE_CLIENT_SCAN_ONLY when ClientModeImpl sends the broadcast. 87 broadcast.send(); 88 } else if (manager.getRole() == ROLE_CLIENT_SECONDARY_TRANSIENT) { 89 // buffer the broadcast until the ClientModeManager becomes primary. 90 mBufferedBroadcasts 91 .computeIfAbsent(manager, k -> new ArrayList<>()) 92 .add(broadcast); 93 } 94 // for all other roles, they will never become primary, so discard their broadcasts 95 } 96 sendAllBroadcasts(ClientModeManager manager)97 private void sendAllBroadcasts(ClientModeManager manager) { 98 List<QueuedBroadcast> queuedBroadcasts = mBufferedBroadcasts.getOrDefault( 99 manager, Collections.emptyList()); 100 for (QueuedBroadcast broadcast : queuedBroadcasts) { 101 broadcast.send(); 102 } 103 // clear the sent broadcasts 104 clearQueue(manager); 105 } 106 107 /** 108 * Clear the broadcast queue for the given manager when e.g. the Make-Before-Break attempt 109 * fails, or the ClientModeManager is deleted. 110 * 111 * TODO(b/174041877): Call this when connection fails during Make Before Break 112 */ clearQueue(@onNull ClientModeManager manager)113 public void clearQueue(@NonNull ClientModeManager manager) { 114 mBufferedBroadcasts.remove(manager); 115 } 116 117 /** 118 * Send broadcasts to fake the disconnection of the previous network, since apps expect there 119 * to be only one connection at a time. 120 */ fakeDisconnectionBroadcasts()121 public void fakeDisconnectionBroadcasts() { 122 ClientModeImpl.sendNetworkChangeBroadcast( 123 mContext, NetworkInfo.DetailedState.DISCONNECTED, mVerboseLoggingEnabled); 124 } 125 126 private class PrimaryClientModeManagerChangedCallback 127 implements ActiveModeWarden.PrimaryClientModeManagerChangedCallback { 128 129 @Override onChange( @ullable ConcreteClientModeManager prevPrimaryClientModeManager, @Nullable ConcreteClientModeManager newPrimaryClientModeManager)130 public void onChange( 131 @Nullable ConcreteClientModeManager prevPrimaryClientModeManager, 132 @Nullable ConcreteClientModeManager newPrimaryClientModeManager) { 133 if (newPrimaryClientModeManager == null) { 134 return; 135 } 136 // when the a ClientModeManager becomes primary, send all its queued broadcasts 137 sendAllBroadcasts(newPrimaryClientModeManager); 138 } 139 } 140 141 private class ModeChangeCallback implements ActiveModeWarden.ModeChangeCallback { 142 143 @Override onActiveModeManagerAdded(@onNull ActiveModeManager activeModeManager)144 public void onActiveModeManagerAdded(@NonNull ActiveModeManager activeModeManager) { 145 // no-op 146 } 147 148 @Override onActiveModeManagerRoleChanged(@onNull ActiveModeManager activeModeManager)149 public void onActiveModeManagerRoleChanged(@NonNull ActiveModeManager activeModeManager) { 150 // no-op 151 } 152 153 @Override onActiveModeManagerRemoved(@onNull ActiveModeManager activeModeManager)154 public void onActiveModeManagerRemoved(@NonNull ActiveModeManager activeModeManager) { 155 if (!(activeModeManager instanceof ClientModeManager)) { 156 return; 157 } 158 ClientModeManager clientModeManager = (ClientModeManager) activeModeManager; 159 clearQueue(clientModeManager); 160 } 161 } 162 } 163