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 package android.net.vcn; 17 18 import static java.util.Objects.requireNonNull; 19 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.RequiresPermission; 24 import android.annotation.SystemApi; 25 import android.annotation.SystemService; 26 import android.content.Context; 27 import android.net.LinkProperties; 28 import android.net.NetworkCapabilities; 29 import android.os.Binder; 30 import android.os.ParcelUuid; 31 import android.os.RemoteException; 32 import android.os.ServiceSpecificException; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.annotations.VisibleForTesting.Visibility; 36 37 import java.io.IOException; 38 import java.lang.annotation.Retention; 39 import java.lang.annotation.RetentionPolicy; 40 import java.util.Collections; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.concurrent.ConcurrentHashMap; 44 import java.util.concurrent.Executor; 45 46 /** 47 * VcnManager publishes APIs for applications to configure and manage Virtual Carrier Networks. 48 * 49 * <p>A VCN creates a virtualization layer to allow carriers to aggregate heterogeneous physical 50 * networks, unifying them as a single carrier network. This enables infrastructure flexibility on 51 * the part of carriers without impacting user connectivity, abstracting the physical network 52 * technologies as an implementation detail of their public network. 53 * 54 * <p>Each VCN virtualizes a carrier's network by building tunnels to a carrier's core network over 55 * carrier-managed physical links and supports a IP mobility layer to ensure seamless transitions 56 * between the underlying networks. Each VCN is configured based on a Subscription Group (see {@link 57 * android.telephony.SubscriptionManager}) and aggregates all networks that are brought up based on 58 * a profile or suggestion in the specified Subscription Group. 59 * 60 * <p>The VCN can be configured to expose one or more {@link android.net.Network}(s), each with 61 * different capabilities, allowing for APN virtualization. 62 * 63 * <p>If a tunnel fails to connect, or otherwise encounters a fatal error, the VCN will attempt to 64 * reestablish the connection. If the tunnel still has not reconnected after a system-determined 65 * timeout, the VCN Safe Mode (see below) will be entered. 66 * 67 * <p>The VCN Safe Mode ensures that users (and carriers) have a fallback to restore system 68 * connectivity to update profiles, diagnose issues, contact support, or perform other remediation 69 * tasks. In Safe Mode, the system will allow underlying cellular networks to be used as default. 70 * Additionally, during Safe Mode, the VCN will continue to retry the connections, and will 71 * automatically exit Safe Mode if all active tunnels connect successfully. 72 */ 73 @SystemService(Context.VCN_MANAGEMENT_SERVICE) 74 public class VcnManager { 75 @NonNull private static final String TAG = VcnManager.class.getSimpleName(); 76 77 /** 78 * Key for WiFi entry RSSI thresholds 79 * 80 * <p>The VCN will only migrate to a Carrier WiFi network that has a signal strength greater 81 * than, or equal to this threshold. 82 * 83 * <p>WARNING: The VCN does not listen for changes to this key made after VCN startup. 84 * 85 * @hide 86 */ 87 @NonNull 88 public static final String VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY = 89 "vcn_network_selection_wifi_entry_rssi_threshold"; 90 91 /** 92 * Key for WiFi entry RSSI thresholds 93 * 94 * <p>If the VCN's selected Carrier WiFi network has a signal strength less than this threshold, 95 * the VCN will attempt to migrate away from the Carrier WiFi network. 96 * 97 * <p>WARNING: The VCN does not listen for changes to this key made after VCN startup. 98 * 99 * @hide 100 */ 101 @NonNull 102 public static final String VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY = 103 "vcn_network_selection_wifi_exit_rssi_threshold"; 104 105 // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz 106 107 /** 108 * Key for transports that need to be marked as restricted by the VCN 109 * 110 * <p>Defaults to TRANSPORT_WIFI if the config does not exist 111 * 112 * @hide 113 */ 114 public static final String VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY = 115 "vcn_restricted_transports"; 116 117 /** 118 * Key for maximum number of parallel SAs for tunnel aggregation 119 * 120 * <p>If set to a value > 1, multiple tunnels will be set up, and inbound traffic will be 121 * aggregated over the various tunnels. 122 * 123 * <p>Defaults to 1, unless overridden by carrier config 124 * 125 * @hide 126 */ 127 @NonNull 128 public static final String VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY = 129 "vcn_tunnel_aggregation_sa_count_max"; 130 131 /** List of Carrier Config options to extract from Carrier Config bundles. @hide */ 132 @NonNull 133 public static final String[] VCN_RELATED_CARRIER_CONFIG_KEYS = 134 new String[] { 135 VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, 136 VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, 137 VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, 138 VCN_TUNNEL_AGGREGATION_SA_COUNT_MAX_KEY, 139 }; 140 141 private static final Map< 142 VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder> 143 REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); 144 145 @NonNull private final Context mContext; 146 @NonNull private final IVcnManagementService mService; 147 148 /** 149 * Construct an instance of VcnManager within an application context. 150 * 151 * @param ctx the application context for this manager 152 * @param service the VcnManagementService binder backing this manager 153 * 154 * @hide 155 */ VcnManager(@onNull Context ctx, @NonNull IVcnManagementService service)156 public VcnManager(@NonNull Context ctx, @NonNull IVcnManagementService service) { 157 mContext = requireNonNull(ctx, "missing context"); 158 mService = requireNonNull(service, "missing service"); 159 } 160 161 /** 162 * Get all currently registered VcnNetworkPolicyChangeListeners for testing purposes. 163 * 164 * @hide 165 */ 166 @VisibleForTesting(visibility = Visibility.PRIVATE) 167 @NonNull 168 public static Map<VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder> getAllPolicyListeners()169 getAllPolicyListeners() { 170 return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS); 171 } 172 173 /** 174 * Sets the VCN configuration for a given subscription group. 175 * 176 * <p>An app that has carrier privileges for any of the subscriptions in the given group may set 177 * a VCN configuration. If a configuration already exists for the given subscription group, it 178 * will be overridden. Any active VCN(s) may be forced to restart to use the new configuration. 179 * 180 * <p>This API is ONLY permitted for callers running as the primary user. 181 * 182 * @param subscriptionGroup the subscription group that the configuration should be applied to 183 * @param config the configuration parameters for the VCN 184 * @throws SecurityException if the caller does not have carrier privileges for the provided 185 * subscriptionGroup, or is not running as the primary user 186 * @throws IOException if the configuration failed to be saved and persisted to disk. This may 187 * occur due to temporary disk errors, or more permanent conditions such as a full disk. 188 */ 189 @RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant setVcnConfig(@onNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config)190 public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) 191 throws IOException { 192 requireNonNull(subscriptionGroup, "subscriptionGroup was null"); 193 requireNonNull(config, "config was null"); 194 195 try { 196 mService.setVcnConfig(subscriptionGroup, config, mContext.getOpPackageName()); 197 } catch (ServiceSpecificException e) { 198 throw new IOException(e); 199 } catch (RemoteException e) { 200 throw e.rethrowFromSystemServer(); 201 } 202 } 203 204 /** 205 * Clears the VCN configuration for a given subscription group. 206 * 207 * <p>An app that has carrier privileges for any of the subscriptions in the given group may 208 * clear a VCN configuration. This API is ONLY permitted for callers running as the primary 209 * user. Any active VCN associated with this configuration will be torn down. 210 * 211 * @param subscriptionGroup the subscription group that the configuration should be applied to 212 * @throws SecurityException if the caller does not have carrier privileges, is not the owner of 213 * the associated configuration, or is not running as the primary user 214 * @throws IOException if the configuration failed to be cleared from disk. This may occur due 215 * to temporary disk errors, or more permanent conditions such as a full disk. 216 */ 217 @RequiresPermission("carrier privileges") // TODO (b/72967236): Define a system-wide constant clearVcnConfig(@onNull ParcelUuid subscriptionGroup)218 public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup) throws IOException { 219 requireNonNull(subscriptionGroup, "subscriptionGroup was null"); 220 221 try { 222 mService.clearVcnConfig(subscriptionGroup, mContext.getOpPackageName()); 223 } catch (ServiceSpecificException e) { 224 throw new IOException(e); 225 } catch (RemoteException e) { 226 throw e.rethrowFromSystemServer(); 227 } 228 } 229 230 /** 231 * Retrieves the list of Subscription Groups for which a VCN Configuration has been set. 232 * 233 * <p>The returned list will include only subscription groups for which an associated {@link 234 * VcnConfig} exists, and the app is either: 235 * 236 * <ul> 237 * <li>Carrier privileged for that subscription group, or 238 * <li>Is the provisioning package of the config 239 * </ul> 240 * 241 * @throws SecurityException if the caller is not running as the primary user 242 */ 243 @NonNull getConfiguredSubscriptionGroups()244 public List<ParcelUuid> getConfiguredSubscriptionGroups() { 245 try { 246 return mService.getConfiguredSubscriptionGroups(mContext.getOpPackageName()); 247 } catch (RemoteException e) { 248 throw e.rethrowFromSystemServer(); 249 } 250 } 251 252 // TODO(b/180537630): remove all VcnUnderlyingNetworkPolicyListener refs once Telephony is using 253 // the new VcnNetworkPolicyChangeListener API 254 /** 255 * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components 256 * can register to receive updates for VCN-underlying Network policies from the System Server. 257 * 258 * @hide 259 */ 260 public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyChangeListener {} 261 262 /** 263 * Add a listener for VCN-underlying network policy updates. 264 * 265 * @param executor the Executor that will be used for invoking all calls to the specified 266 * Listener 267 * @param listener the VcnUnderlyingNetworkPolicyListener to be added 268 * @throws SecurityException if the caller does not have permission NETWORK_FACTORY 269 * @throws IllegalStateException if the specified VcnUnderlyingNetworkPolicyListener is already 270 * registered 271 * @hide 272 */ 273 @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) addVcnUnderlyingNetworkPolicyListener( @onNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener)274 public void addVcnUnderlyingNetworkPolicyListener( 275 @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) { 276 addVcnNetworkPolicyChangeListener(executor, listener); 277 } 278 279 /** 280 * Remove the specified VcnUnderlyingNetworkPolicyListener from VcnManager. 281 * 282 * <p>If the specified listener is not currently registered, this is a no-op. 283 * 284 * @param listener the VcnUnderlyingNetworkPolicyListener that will be removed 285 * @hide 286 */ removeVcnUnderlyingNetworkPolicyListener( @onNull VcnUnderlyingNetworkPolicyListener listener)287 public void removeVcnUnderlyingNetworkPolicyListener( 288 @NonNull VcnUnderlyingNetworkPolicyListener listener) { 289 removeVcnNetworkPolicyChangeListener(listener); 290 } 291 292 /** 293 * Queries the underlying network policy for a network with the given parameters. 294 * 295 * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy 296 * may have changed via {@link VcnUnderlyingNetworkPolicyListener#onPolicyChanged()}, a Network 297 * Provider MUST poll for the updated Network policy based on that Network's capabilities and 298 * properties. 299 * 300 * @param networkCapabilities the NetworkCapabilities to be used in determining the Network 301 * policy for this Network. 302 * @param linkProperties the LinkProperties to be used in determining the Network policy for 303 * this Network. 304 * @throws SecurityException if the caller does not have permission NETWORK_FACTORY 305 * @return the VcnUnderlyingNetworkPolicy to be used for this Network. 306 * @hide 307 */ 308 @NonNull 309 @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) getUnderlyingNetworkPolicy( @onNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties)310 public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy( 311 @NonNull NetworkCapabilities networkCapabilities, 312 @NonNull LinkProperties linkProperties) { 313 requireNonNull(networkCapabilities, "networkCapabilities must not be null"); 314 requireNonNull(linkProperties, "linkProperties must not be null"); 315 316 try { 317 return mService.getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); 318 } catch (RemoteException e) { 319 throw e.rethrowFromSystemServer(); 320 } 321 } 322 323 /** 324 * VcnNetworkPolicyChangeListener is the interface through which internal system components 325 * (e.g. Network Factories) can register to receive updates for VCN-underlying Network policies 326 * from the System Server. 327 * 328 * <p>Any Network Factory that brings up Networks capable of being VCN-underlying Networks 329 * should register a VcnNetworkPolicyChangeListener. VcnManager will then use this listener to 330 * notify the registrant when VCN Network policies change. Upon receiving this signal, the 331 * listener must check {@link VcnManager} for the current Network policy result for each of its 332 * Networks via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}. 333 * 334 * @hide 335 */ 336 @SystemApi 337 public interface VcnNetworkPolicyChangeListener { 338 /** 339 * Notifies the implementation that the VCN's underlying Network policy has changed. 340 * 341 * <p>After receiving this callback, implementations should get the current {@link 342 * VcnNetworkPolicyResult} via {@link #applyVcnNetworkPolicy(NetworkCapabilities, 343 * LinkProperties)}. 344 */ onPolicyChanged()345 void onPolicyChanged(); 346 } 347 348 /** 349 * Add a listener for VCN-underlying Network policy updates. 350 * 351 * <p>A {@link VcnNetworkPolicyChangeListener} is eligible to begin receiving callbacks once it 352 * is registered. No callbacks are guaranteed upon registration. 353 * 354 * @param executor the Executor that will be used for invoking all calls to the specified 355 * Listener 356 * @param listener the VcnNetworkPolicyChangeListener to be added 357 * @throws SecurityException if the caller does not have permission NETWORK_FACTORY 358 * @throws IllegalStateException if the specified VcnNetworkPolicyChangeListener is already 359 * registered 360 * @hide 361 */ 362 @SystemApi 363 @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) addVcnNetworkPolicyChangeListener( @onNull Executor executor, @NonNull VcnNetworkPolicyChangeListener listener)364 public void addVcnNetworkPolicyChangeListener( 365 @NonNull Executor executor, @NonNull VcnNetworkPolicyChangeListener listener) { 366 requireNonNull(executor, "executor must not be null"); 367 requireNonNull(listener, "listener must not be null"); 368 369 VcnUnderlyingNetworkPolicyListenerBinder binder = 370 new VcnUnderlyingNetworkPolicyListenerBinder(executor, listener); 371 if (REGISTERED_POLICY_LISTENERS.putIfAbsent(listener, binder) != null) { 372 throw new IllegalStateException("listener is already registered with VcnManager"); 373 } 374 375 try { 376 mService.addVcnUnderlyingNetworkPolicyListener(binder); 377 } catch (RemoteException e) { 378 REGISTERED_POLICY_LISTENERS.remove(listener); 379 throw e.rethrowFromSystemServer(); 380 } 381 } 382 383 /** 384 * Remove the specified VcnNetworkPolicyChangeListener from VcnManager. 385 * 386 * <p>If the specified listener is not currently registered, this is a no-op. 387 * 388 * @param listener the VcnNetworkPolicyChangeListener that will be removed 389 * @throws SecurityException if the caller does not have permission NETWORK_FACTORY 390 * @hide 391 */ 392 @SystemApi 393 @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) removeVcnNetworkPolicyChangeListener( @onNull VcnNetworkPolicyChangeListener listener)394 public void removeVcnNetworkPolicyChangeListener( 395 @NonNull VcnNetworkPolicyChangeListener listener) { 396 requireNonNull(listener, "listener must not be null"); 397 398 VcnUnderlyingNetworkPolicyListenerBinder binder = 399 REGISTERED_POLICY_LISTENERS.remove(listener); 400 if (binder == null) { 401 return; 402 } 403 404 try { 405 mService.removeVcnUnderlyingNetworkPolicyListener(binder); 406 } catch (RemoteException e) { 407 throw e.rethrowFromSystemServer(); 408 } 409 } 410 411 /** 412 * Applies the network policy for a {@link android.net.Network} with the given parameters. 413 * 414 * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy 415 * may have changed via {@link VcnNetworkPolicyChangeListener#onPolicyChanged()}, a Network 416 * Provider MUST poll for the updated Network policy based on that Network's capabilities and 417 * properties. 418 * 419 * @param networkCapabilities the NetworkCapabilities to be used in determining the Network 420 * policy result for this Network. 421 * @param linkProperties the LinkProperties to be used in determining the Network policy result 422 * for this Network. 423 * @throws SecurityException if the caller does not have permission NETWORK_FACTORY 424 * @return the {@link VcnNetworkPolicyResult} to be used for this Network. 425 * @hide 426 */ 427 @NonNull 428 @SystemApi 429 @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) applyVcnNetworkPolicy( @onNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties)430 public VcnNetworkPolicyResult applyVcnNetworkPolicy( 431 @NonNull NetworkCapabilities networkCapabilities, 432 @NonNull LinkProperties linkProperties) { 433 requireNonNull(networkCapabilities, "networkCapabilities must not be null"); 434 requireNonNull(linkProperties, "linkProperties must not be null"); 435 436 final VcnUnderlyingNetworkPolicy policy = 437 getUnderlyingNetworkPolicy(networkCapabilities, linkProperties); 438 return new VcnNetworkPolicyResult( 439 policy.isTeardownRequested(), policy.getMergedNetworkCapabilities()); 440 } 441 442 /** @hide */ 443 @Retention(RetentionPolicy.SOURCE) 444 @IntDef({ 445 VCN_STATUS_CODE_NOT_CONFIGURED, 446 VCN_STATUS_CODE_INACTIVE, 447 VCN_STATUS_CODE_ACTIVE, 448 VCN_STATUS_CODE_SAFE_MODE 449 }) 450 public @interface VcnStatusCode {} 451 452 /** 453 * Value indicating that the VCN for the subscription group is not configured, or that the 454 * callback is not privileged for the subscription group. 455 */ 456 public static final int VCN_STATUS_CODE_NOT_CONFIGURED = 0; 457 458 /** 459 * Value indicating that the VCN for the subscription group is inactive. 460 * 461 * <p>A VCN is inactive if a {@link VcnConfig} is present for the subscription group, but the 462 * provisioning package is not privileged. 463 */ 464 public static final int VCN_STATUS_CODE_INACTIVE = 1; 465 466 /** 467 * Value indicating that the VCN for the subscription group is active. 468 * 469 * <p>A VCN is active if a {@link VcnConfig} is present for the subscription, the provisioning 470 * package is privileged, and the VCN is not in Safe Mode. In other words, a VCN is considered 471 * active while it is connecting, fully connected, and disconnecting. 472 */ 473 public static final int VCN_STATUS_CODE_ACTIVE = 2; 474 475 /** 476 * Value indicating that the VCN for the subscription group is in Safe Mode. 477 * 478 * <p>A VCN will be put into Safe Mode if any of the gateway connections were unable to 479 * establish a connection within a system-determined timeout (while underlying networks were 480 * available). 481 */ 482 public static final int VCN_STATUS_CODE_SAFE_MODE = 3; 483 484 /** @hide */ 485 @Retention(RetentionPolicy.SOURCE) 486 @IntDef({ 487 VCN_ERROR_CODE_INTERNAL_ERROR, 488 VCN_ERROR_CODE_CONFIG_ERROR, 489 VCN_ERROR_CODE_NETWORK_ERROR 490 }) 491 public @interface VcnErrorCode {} 492 493 /** 494 * Value indicating that an internal failure occurred in this Gateway Connection. 495 */ 496 public static final int VCN_ERROR_CODE_INTERNAL_ERROR = 0; 497 498 /** 499 * Value indicating that an error with this Gateway Connection's configuration occurred. 500 * 501 * <p>For example, this error code will be returned after authentication failures. 502 */ 503 public static final int VCN_ERROR_CODE_CONFIG_ERROR = 1; 504 505 /** 506 * Value indicating that a Network error occurred with this Gateway Connection. 507 * 508 * <p>For example, this error code will be returned if an underlying {@link android.net.Network} 509 * for this Gateway Connection is lost, or if an error occurs while resolving the connection 510 * endpoint address. 511 */ 512 public static final int VCN_ERROR_CODE_NETWORK_ERROR = 2; 513 514 /** 515 * VcnStatusCallback is the interface for Carrier apps to receive updates for their VCNs. 516 * 517 * <p>VcnStatusCallbacks may be registered before {@link VcnConfig}s are provided for a 518 * subscription group. 519 */ 520 public abstract static class VcnStatusCallback { 521 private VcnStatusCallbackBinder mCbBinder; 522 523 /** 524 * Invoked when status of the VCN for this callback's subscription group changes. 525 * 526 * @param statusCode the code for the status change encountered by this {@link 527 * VcnStatusCallback}'s subscription group. This value will be one of VCN_STATUS_CODE_*. 528 */ onStatusChanged(@cnStatusCode int statusCode)529 public abstract void onStatusChanged(@VcnStatusCode int statusCode); 530 531 /** 532 * Invoked when a VCN Gateway Connection corresponding to this callback's subscription group 533 * encounters an error. 534 * 535 * @param gatewayConnectionName the String GatewayConnection name for the GatewayConnection 536 * encountering an error. This will match the name for exactly one {@link 537 * VcnGatewayConnectionConfig} for the {@link VcnConfig} configured for this callback's 538 * subscription group 539 * @param errorCode the code to indicate the error that occurred. This value will be one of 540 * VCN_ERROR_CODE_*. 541 * @param detail Throwable to provide additional information about the error, or {@code 542 * null} if none 543 */ onGatewayConnectionError( @onNull String gatewayConnectionName, @VcnErrorCode int errorCode, @Nullable Throwable detail)544 public abstract void onGatewayConnectionError( 545 @NonNull String gatewayConnectionName, 546 @VcnErrorCode int errorCode, 547 @Nullable Throwable detail); 548 } 549 550 /** 551 * Registers the given callback to receive status updates for the specified subscription. 552 * 553 * <p>Callbacks can be registered for a subscription before {@link VcnConfig}s are set for it. 554 * 555 * <p>A {@link VcnStatusCallback} may only be registered for one subscription at a time. {@link 556 * VcnStatusCallback}s may be reused once unregistered. 557 * 558 * <p>A {@link VcnStatusCallback} will only be invoked if the registering package has carrier 559 * privileges for the specified subscription at the time of invocation. 560 * 561 * <p>A {@link VcnStatusCallback} is eligible to begin receiving callbacks once it is registered 562 * and there is a VCN active for its specified subscription group (this may happen after the 563 * callback is registered). 564 * 565 * <p>{@link VcnStatusCallback#onStatusChanged(int)} will be invoked on registration with the 566 * current status for the specified subscription group's VCN. If the registrant is not 567 * privileged for this subscription group, {@link #VCN_STATUS_CODE_NOT_CONFIGURED} will be 568 * returned. 569 * 570 * @param subscriptionGroup The subscription group to match for callbacks 571 * @param executor The {@link Executor} to be used for invoking callbacks 572 * @param callback The VcnStatusCallback to be registered 573 * @throws IllegalStateException if callback is currently registered with VcnManager 574 */ registerVcnStatusCallback( @onNull ParcelUuid subscriptionGroup, @NonNull Executor executor, @NonNull VcnStatusCallback callback)575 public void registerVcnStatusCallback( 576 @NonNull ParcelUuid subscriptionGroup, 577 @NonNull Executor executor, 578 @NonNull VcnStatusCallback callback) { 579 requireNonNull(subscriptionGroup, "subscriptionGroup must not be null"); 580 requireNonNull(executor, "executor must not be null"); 581 requireNonNull(callback, "callback must not be null"); 582 583 synchronized (callback) { 584 if (callback.mCbBinder != null) { 585 throw new IllegalStateException("callback is already registered with VcnManager"); 586 } 587 callback.mCbBinder = new VcnStatusCallbackBinder(executor, callback); 588 589 try { 590 mService.registerVcnStatusCallback( 591 subscriptionGroup, callback.mCbBinder, mContext.getOpPackageName()); 592 } catch (RemoteException e) { 593 callback.mCbBinder = null; 594 throw e.rethrowFromSystemServer(); 595 } 596 } 597 } 598 599 /** 600 * Unregisters the given callback. 601 * 602 * <p>Once unregistered, the callback will stop receiving status updates for the subscription it 603 * was registered with. 604 * 605 * @param callback The callback to be unregistered 606 */ unregisterVcnStatusCallback(@onNull VcnStatusCallback callback)607 public void unregisterVcnStatusCallback(@NonNull VcnStatusCallback callback) { 608 requireNonNull(callback, "callback must not be null"); 609 610 synchronized (callback) { 611 if (callback.mCbBinder == null) { 612 // no Binder attached to this callback, so it's not currently registered 613 return; 614 } 615 616 try { 617 mService.unregisterVcnStatusCallback(callback.mCbBinder); 618 } catch (RemoteException e) { 619 throw e.rethrowFromSystemServer(); 620 } finally { 621 callback.mCbBinder = null; 622 } 623 } 624 } 625 626 /** 627 * Binder wrapper for added VcnNetworkPolicyChangeListeners to receive signals from System 628 * Server. 629 * 630 * @hide 631 */ 632 private static class VcnUnderlyingNetworkPolicyListenerBinder 633 extends IVcnUnderlyingNetworkPolicyListener.Stub { 634 @NonNull private final Executor mExecutor; 635 @NonNull private final VcnNetworkPolicyChangeListener mListener; 636 VcnUnderlyingNetworkPolicyListenerBinder( Executor executor, VcnNetworkPolicyChangeListener listener)637 private VcnUnderlyingNetworkPolicyListenerBinder( 638 Executor executor, VcnNetworkPolicyChangeListener listener) { 639 mExecutor = executor; 640 mListener = listener; 641 } 642 643 @Override onPolicyChanged()644 public void onPolicyChanged() { 645 Binder.withCleanCallingIdentity( 646 () -> mExecutor.execute(() -> mListener.onPolicyChanged())); 647 } 648 } 649 650 /** 651 * Binder wrapper for VcnStatusCallbacks to receive signals from VcnManagementService. 652 * 653 * @hide 654 */ 655 @VisibleForTesting(visibility = Visibility.PRIVATE) 656 public static class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub { 657 @NonNull private final Executor mExecutor; 658 @NonNull private final VcnStatusCallback mCallback; 659 VcnStatusCallbackBinder( @onNull Executor executor, @NonNull VcnStatusCallback callback)660 public VcnStatusCallbackBinder( 661 @NonNull Executor executor, @NonNull VcnStatusCallback callback) { 662 mExecutor = executor; 663 mCallback = callback; 664 } 665 666 @Override onVcnStatusChanged(@cnStatusCode int statusCode)667 public void onVcnStatusChanged(@VcnStatusCode int statusCode) { 668 Binder.withCleanCallingIdentity( 669 () -> mExecutor.execute(() -> mCallback.onStatusChanged(statusCode))); 670 } 671 672 // TODO(b/180521637): use ServiceSpecificException for safer Exception 'parceling' 673 @Override onGatewayConnectionError( @onNull String gatewayConnectionName, @VcnErrorCode int errorCode, @Nullable String exceptionClass, @Nullable String exceptionMessage)674 public void onGatewayConnectionError( 675 @NonNull String gatewayConnectionName, 676 @VcnErrorCode int errorCode, 677 @Nullable String exceptionClass, 678 @Nullable String exceptionMessage) { 679 final Throwable cause = createThrowableByClassName(exceptionClass, exceptionMessage); 680 681 Binder.withCleanCallingIdentity( 682 () -> 683 mExecutor.execute( 684 () -> 685 mCallback.onGatewayConnectionError( 686 gatewayConnectionName, errorCode, cause))); 687 } 688 createThrowableByClassName( @ullable String className, @Nullable String message)689 private static Throwable createThrowableByClassName( 690 @Nullable String className, @Nullable String message) { 691 if (className == null) { 692 return null; 693 } 694 695 try { 696 Class<?> c = Class.forName(className); 697 return (Throwable) c.getConstructor(String.class).newInstance(message); 698 } catch (ReflectiveOperationException | ClassCastException e) { 699 return new RuntimeException(className + ": " + message); 700 } 701 } 702 } 703 } 704