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