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