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.connectivity; 18 19 import android.annotation.NonNull; 20 import android.net.INetworkOfferCallback; 21 import android.net.NetworkCapabilities; 22 import android.net.NetworkRequest; 23 import android.os.RemoteException; 24 25 import java.util.ArrayList; 26 import java.util.HashSet; 27 import java.util.Objects; 28 import java.util.Set; 29 30 /** 31 * Represents an offer made by a NetworkProvider to create a network if a need arises. 32 * 33 * This class contains the prospective score and capabilities of the network. The provider 34 * is not obligated to caps able to create a network satisfying this, nor to build a network 35 * with the exact score and/or capabilities passed ; after all, not all providers know in 36 * advance what a network will look like after it's connected. Instead, this is meant as a 37 * filter to limit requests sent to the provider by connectivity to those that this offer stands 38 * a chance to fulfill. 39 * 40 * @see NetworkProvider#offerNetwork. 41 * 42 * @hide 43 */ 44 public class NetworkOffer implements NetworkRanker.Scoreable { 45 private static final String TAG = NetworkOffer.class.getSimpleName(); 46 @NonNull public final FullScore score; 47 @NonNull public final NetworkCapabilities caps; 48 @NonNull public final INetworkOfferCallback callback; 49 @NonNull public final int providerId; 50 // While this could, in principle, be deduced from the old values of the satisfying networks, 51 // doing so would add a lot of complexity and performance penalties. For each request, the 52 // ranker would have to run again to figure out if this offer used to be able to beat the 53 // previous satisfier to know if there is a change in whether this offer is now needed ; 54 // besides, there would be a need to handle an edge case when a new request comes online, 55 // where it's not satisfied before the first rematch, where starting to satisfy a request 56 // should not result in sending unneeded to this offer. This boolean, while requiring that 57 // the offers are only ever manipulated on the CS thread, is by far a simpler and 58 // economical solution. 59 private final Set<NetworkRequest> mCurrentlyNeeded = new HashSet<>(); 60 NetworkOffer(@onNull final FullScore score, @NonNull final NetworkCapabilities caps, @NonNull final INetworkOfferCallback callback, @NonNull final int providerId)61 public NetworkOffer(@NonNull final FullScore score, 62 @NonNull final NetworkCapabilities caps, 63 @NonNull final INetworkOfferCallback callback, 64 @NonNull final int providerId) { 65 this.score = Objects.requireNonNull(score); 66 this.caps = Objects.requireNonNull(caps); 67 this.callback = Objects.requireNonNull(callback); 68 this.providerId = providerId; 69 } 70 71 /** 72 * Get the score filter of this offer 73 */ getScore()74 @Override @NonNull public FullScore getScore() { 75 return score; 76 } 77 78 /** 79 * Get the capabilities filter of this offer 80 */ getCapsNoCopy()81 @Override @NonNull public NetworkCapabilities getCapsNoCopy() { 82 return caps; 83 } 84 85 /** 86 * Tell the provider for this offer that the network is needed for a request. 87 * @param request the request for which the offer is needed 88 */ onNetworkNeeded(@onNull final NetworkRequest request)89 public void onNetworkNeeded(@NonNull final NetworkRequest request) { 90 if (mCurrentlyNeeded.contains(request)) { 91 throw new IllegalStateException("Network already needed"); 92 } 93 mCurrentlyNeeded.add(request); 94 try { 95 callback.onNetworkNeeded(request); 96 } catch (final RemoteException e) { 97 // The provider is dead. It will be removed by the death recipient. 98 } 99 } 100 101 /** 102 * Tell the provider for this offer that the network is no longer needed for this request. 103 * 104 * onNetworkNeeded will have been called with the same request before. 105 * 106 * @param request the request 107 */ onNetworkUnneeded(@onNull final NetworkRequest request)108 public void onNetworkUnneeded(@NonNull final NetworkRequest request) { 109 if (!mCurrentlyNeeded.contains(request)) { 110 throw new IllegalStateException("Network already unneeded"); 111 } 112 mCurrentlyNeeded.remove(request); 113 try { 114 callback.onNetworkUnneeded(request); 115 } catch (final RemoteException e) { 116 // The provider is dead. It will be removed by the death recipient. 117 } 118 } 119 120 /** 121 * Returns whether this offer is currently needed for this request. 122 * @param request the request 123 * @return whether the offer is currently considered needed 124 */ neededFor(@onNull final NetworkRequest request)125 public boolean neededFor(@NonNull final NetworkRequest request) { 126 return mCurrentlyNeeded.contains(request); 127 } 128 129 /** 130 * Sends onNetworkUnneeded for any remaining NetworkRequests. 131 * 132 * Used after a NetworkOffer migration failed to let the provider know that its networks should 133 * be torn down (as the offer is no longer registered). 134 */ notifyUnneeded()135 public void notifyUnneeded() { 136 try { 137 for (NetworkRequest request : mCurrentlyNeeded) { 138 callback.onNetworkUnneeded(request); 139 } 140 } catch (RemoteException e) { 141 // The remote is dead; nothing to do. 142 } 143 mCurrentlyNeeded.clear(); 144 } 145 146 /** 147 * Migrate from, and take over, a previous offer. 148 * 149 * When an updated offer is sent from a provider, call this method on the new offer, passing 150 * the old one, to take over the state. 151 * 152 * @param previousOffer the previous offer 153 */ migrateFrom(@onNull final NetworkOffer previousOffer)154 public void migrateFrom(@NonNull final NetworkOffer previousOffer) { 155 if (!callback.asBinder().equals(previousOffer.callback.asBinder())) { 156 throw new IllegalArgumentException("Can only migrate from a previous version of" 157 + " the same offer"); 158 } 159 mCurrentlyNeeded.clear(); 160 mCurrentlyNeeded.addAll(previousOffer.mCurrentlyNeeded); 161 } 162 163 @Override toString()164 public String toString() { 165 final ArrayList<Integer> neededRequestIds = new ArrayList<>(); 166 for (final NetworkRequest request : mCurrentlyNeeded) { 167 neededRequestIds.add(request.requestId); 168 } 169 return "NetworkOffer [ Provider Id (" + providerId + ") " + score + " Caps " 170 + caps + " Needed by " + neededRequestIds + "]"; 171 } 172 } 173