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