1 /* 2 * Copyright (C) 2021 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 android.annotation.NonNull; 20 import android.net.wifi.IScoreUpdateObserver; 21 import android.net.wifi.WifiManager; 22 import android.util.Log; 23 24 import com.android.modules.utils.build.SdkLevel; 25 26 27 /** 28 * This is the callback proxy used to listen to external scorer actions triggered via 29 * {@link android.net.wifi.WifiManager.ScoreUpdateObserver}. 30 * 31 * <p> 32 * Note: 33 * <li>This was extracted out of {@link WifiScoreReport} class since that is not a singleton and 34 * is associated with a {@link ClientModeImpl} instance. However, this proxy sent to external scorer 35 * needs to be a singleton (i.e we cannot send the external scorer a new proxy every time 36 * a new primary {@link ClientModeImpl} is created).</li> 37 * <li> Whenever a new primary CMM is created, it needs to register to this proxy to listen for 38 * actions from the external scorer.</li> 39 * </p> 40 */ 41 public class ExternalScoreUpdateObserverProxy extends IScoreUpdateObserver.Stub { 42 private static final String TAG = "WifiExternalScoreUpdateObserverProxy"; 43 44 private final WifiThreadRunner mWifiThreadRunner; 45 private WifiManager.ScoreUpdateObserver mCallback; 46 private int mCountNullCallback = 0; 47 private static final int MAX_NULL_CALLBACK_TRIGGER_WTF = 3; 48 ExternalScoreUpdateObserverProxy(WifiThreadRunner wifiThreadRunner)49 ExternalScoreUpdateObserverProxy(WifiThreadRunner wifiThreadRunner) { 50 mWifiThreadRunner = wifiThreadRunner; 51 } 52 53 /** 54 * Register a new callback to listen for events from external scorer. 55 */ registerCallback(@onNull WifiManager.ScoreUpdateObserver callback)56 public void registerCallback(@NonNull WifiManager.ScoreUpdateObserver callback) { 57 if (mCallback != null) { 58 Log.i(TAG, "Replacing an existing callback (new primary CMM created)"); 59 } 60 mCallback = callback; 61 } 62 63 /** 64 * Unregister callback to listen for events from external scorer. 65 */ unregisterCallback(@onNull WifiManager.ScoreUpdateObserver callback)66 public void unregisterCallback(@NonNull WifiManager.ScoreUpdateObserver callback) { 67 // mCallback can be overwritten by another CMM, when we remove it we should only 68 // remove if it is the most recently registered mCallback 69 if (mCallback == callback) { 70 mCallback = null; 71 } 72 } 73 incrementAndMaybeLogWtf(String message)74 private void incrementAndMaybeLogWtf(String message) { 75 mCountNullCallback++; 76 if (mCountNullCallback >= MAX_NULL_CALLBACK_TRIGGER_WTF) { 77 Log.wtf(TAG, message, null); 78 } 79 } 80 81 @Override notifyScoreUpdate(int sessionId, int score)82 public void notifyScoreUpdate(int sessionId, int score) { 83 mWifiThreadRunner.post(() -> { 84 if (mCallback == null) { 85 incrementAndMaybeLogWtf("No callback registered, dropping notifyScoreUpdate"); 86 return; 87 } 88 mCountNullCallback = 0; 89 mCallback.notifyScoreUpdate(sessionId, score); 90 }, TAG + "#notifyScoreUpdate"); 91 } 92 93 @Override triggerUpdateOfWifiUsabilityStats(int sessionId)94 public void triggerUpdateOfWifiUsabilityStats(int sessionId) { 95 mWifiThreadRunner.post(() -> { 96 if (mCallback == null) { 97 incrementAndMaybeLogWtf("No callback registered, " 98 + "dropping triggerUpdateOfWifiUsability"); 99 return; 100 } 101 mCountNullCallback = 0; 102 mCallback.triggerUpdateOfWifiUsabilityStats(sessionId); 103 }, TAG + "#triggerUpdateOfWifiUsabilityStats"); 104 } 105 106 @Override notifyStatusUpdate(int sessionId, boolean isUsable)107 public void notifyStatusUpdate(int sessionId, boolean isUsable) { 108 if (!SdkLevel.isAtLeastS()) { 109 throw new UnsupportedOperationException(); 110 } 111 mWifiThreadRunner.post(() -> { 112 if (mCallback == null) { 113 incrementAndMaybeLogWtf("No callback registered, dropping notifyStatusUpdate"); 114 return; 115 } 116 mCountNullCallback = 0; 117 mCallback.notifyStatusUpdate(sessionId, isUsable); 118 }, TAG + "#notifyStatusUpdate"); 119 } 120 121 @Override requestNudOperation(int sessionId)122 public void requestNudOperation(int sessionId) { 123 if (!SdkLevel.isAtLeastS()) { 124 throw new UnsupportedOperationException(); 125 } 126 mWifiThreadRunner.post(() -> { 127 if (mCallback == null) { 128 incrementAndMaybeLogWtf("No callback registered, dropping requestNudOperation"); 129 return; 130 } 131 mCountNullCallback = 0; 132 mCallback.requestNudOperation(sessionId); 133 }, TAG + "#requestNudOperation"); 134 } 135 136 @Override blocklistCurrentBssid(int sessionId)137 public void blocklistCurrentBssid(int sessionId) { 138 if (!SdkLevel.isAtLeastS()) { 139 throw new UnsupportedOperationException(); 140 } 141 mWifiThreadRunner.post(() -> { 142 if (mCallback == null) { 143 incrementAndMaybeLogWtf("No callback registered, dropping blocklistCurrentBssid"); 144 return; 145 } 146 mCountNullCallback = 0; 147 mCallback.blocklistCurrentBssid(sessionId); 148 }, TAG + "#blocklistCurrentBssid"); 149 } 150 } 151