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 17 package com.android.server; 18 19 import static android.Manifest.permission.DUMP; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; 21 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 22 import static android.net.NetworkCapabilities.TRANSPORT_TEST; 23 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 24 import static android.net.vcn.VcnGatewayConnectionConfig.ALLOWED_CAPABILITIES; 25 import static android.net.vcn.VcnManager.VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY; 26 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; 27 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE; 28 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED; 29 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; 30 import static android.telephony.SubscriptionManager.isValidSubscriptionId; 31 32 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; 33 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback; 34 35 import static java.util.Objects.requireNonNull; 36 37 import android.annotation.NonNull; 38 import android.annotation.Nullable; 39 import android.app.AppOpsManager; 40 import android.content.BroadcastReceiver; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.content.IntentFilter; 44 import android.content.pm.PackageManager; 45 import android.net.ConnectivityManager; 46 import android.net.LinkProperties; 47 import android.net.Network; 48 import android.net.NetworkCapabilities; 49 import android.net.NetworkRequest; 50 import android.net.vcn.IVcnManagementService; 51 import android.net.vcn.IVcnStatusCallback; 52 import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; 53 import android.net.vcn.VcnConfig; 54 import android.net.vcn.VcnManager.VcnErrorCode; 55 import android.net.vcn.VcnManager.VcnStatusCode; 56 import android.net.vcn.VcnUnderlyingNetworkPolicy; 57 import android.net.wifi.WifiInfo; 58 import android.os.Binder; 59 import android.os.Build; 60 import android.os.Environment; 61 import android.os.Handler; 62 import android.os.HandlerThread; 63 import android.os.IBinder; 64 import android.os.Looper; 65 import android.os.ParcelUuid; 66 import android.os.PersistableBundle; 67 import android.os.Process; 68 import android.os.RemoteException; 69 import android.os.ServiceSpecificException; 70 import android.os.UserHandle; 71 import android.telephony.SubscriptionInfo; 72 import android.telephony.SubscriptionManager; 73 import android.telephony.TelephonyManager; 74 import android.util.ArrayMap; 75 import android.util.ArraySet; 76 import android.util.LocalLog; 77 import android.util.Log; 78 import android.util.Slog; 79 80 import com.android.internal.annotations.GuardedBy; 81 import com.android.internal.annotations.VisibleForTesting; 82 import com.android.internal.annotations.VisibleForTesting.Visibility; 83 import com.android.internal.util.IndentingPrintWriter; 84 import com.android.net.module.util.LocationPermissionChecker; 85 import com.android.net.module.util.PermissionUtils; 86 import com.android.server.vcn.TelephonySubscriptionTracker; 87 import com.android.server.vcn.Vcn; 88 import com.android.server.vcn.VcnContext; 89 import com.android.server.vcn.VcnNetworkProvider; 90 import com.android.server.vcn.util.PersistableBundleUtils; 91 import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; 92 93 import java.io.File; 94 import java.io.FileDescriptor; 95 import java.io.IOException; 96 import java.io.PrintWriter; 97 import java.util.ArrayList; 98 import java.util.Collections; 99 import java.util.Iterator; 100 import java.util.List; 101 import java.util.Map; 102 import java.util.Map.Entry; 103 import java.util.Objects; 104 import java.util.Set; 105 import java.util.concurrent.TimeUnit; 106 107 /** 108 * VcnManagementService manages Virtual Carrier Network profiles and lifecycles. 109 * 110 * <pre>The internal structure of the VCN Management subsystem is as follows: 111 * 112 * +-------------------------+ 1:1 +--------------------------------+ 113 * | VcnManagementService | ------------ Creates ------------> | TelephonySubscriptionManager | 114 * | | | | 115 * | Manages configs and | | Tracks subscriptions, carrier | 116 * | Vcn instance lifecycles | <--- Notifies of subscription & -- | privilege changes, caches maps | 117 * +-------------------------+ carrier privilege changes +--------------------------------+ 118 * | 1:N ^ 119 * | | 120 * | +-------------------------------+ 121 * +---------------+ | 122 * | | 123 * Creates when config present, | 124 * subscription group active, and | 125 * providing app is carrier privileged Notifies of safe 126 * | mode state changes 127 * v | 128 * +-----------------------------------------------------------------------+ 129 * | Vcn | 130 * | | 131 * | Manages GatewayConnection lifecycles based on fulfillable | 132 * | NetworkRequest(s) and overall safe-mode | 133 * +-----------------------------------------------------------------------+ 134 * | 1:N ^ 135 * Creates to fulfill | 136 * NetworkRequest(s), tears Notifies of VcnGatewayConnection 137 * down when no longer needed teardown (e.g. Network reaped) 138 * | and safe-mode timer changes 139 * v | 140 * +-----------------------------------------------------------------------+ 141 * | VcnGatewayConnection | 142 * | | 143 * | Manages a single (IKEv2) tunnel session and NetworkAgent, | 144 * | handles mobility events, (IPsec) Tunnel setup and safe-mode timers | 145 * +-----------------------------------------------------------------------+ 146 * | 1:1 ^ 147 * | | 148 * Creates upon instantiation Notifies of changes in 149 * | selected underlying network 150 * | or its properties 151 * v | 152 * +-----------------------------------------------------------------------+ 153 * | UnderlyingNetworkController | 154 * | | 155 * | Manages lifecycle of underlying physical networks, filing requests to | 156 * | bring them up, and releasing them as they become no longer necessary | 157 * +-----------------------------------------------------------------------+ 158 * </pre> 159 * 160 * @hide 161 */ 162 // TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity 163 public class VcnManagementService extends IVcnManagementService.Stub { 164 @NonNull private static final String TAG = VcnManagementService.class.getSimpleName(); 165 @NonNull private static final String CONTEXT_ATTRIBUTION_TAG = "VCN"; 166 167 private static final long DUMP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5); 168 private static final int LOCAL_LOG_LINE_COUNT = 512; 169 170 private static final Set<Integer> RESTRICTED_TRANSPORTS_DEFAULT = 171 Collections.singleton(TRANSPORT_WIFI); 172 173 // Public for use in all other VCN classes 174 @NonNull public static final LocalLog LOCAL_LOG = new LocalLog(LOCAL_LOG_LINE_COUNT); 175 176 public static final boolean VDBG = false; // STOPSHIP: if true 177 178 @VisibleForTesting(visibility = Visibility.PRIVATE) 179 static final String VCN_CONFIG_FILE = 180 new File(Environment.getDataSystemDirectory(), "vcn/configs.xml").getPath(); 181 182 // TODO(b/176956496): Directly use CarrierServiceBindHelper.UNBIND_DELAY_MILLIS 183 @VisibleForTesting(visibility = Visibility.PRIVATE) 184 static final long CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS = TimeUnit.SECONDS.toMillis(30); 185 186 /* Binder context for this service */ 187 @NonNull private final Context mContext; 188 @NonNull private final Dependencies mDeps; 189 190 @NonNull private final Looper mLooper; 191 @NonNull private final Handler mHandler; 192 @NonNull private final VcnNetworkProvider mNetworkProvider; 193 @NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb; 194 @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker; 195 @NonNull private final BroadcastReceiver mVcnBroadcastReceiver; 196 197 @NonNull 198 private final TrackingNetworkCallback mTrackingNetworkCallback = new TrackingNetworkCallback(); 199 200 @GuardedBy("mLock") 201 @NonNull 202 private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>(); 203 204 @GuardedBy("mLock") 205 @NonNull 206 private final Map<ParcelUuid, Vcn> mVcns = new ArrayMap<>(); 207 208 @GuardedBy("mLock") 209 @NonNull 210 private TelephonySubscriptionSnapshot mLastSnapshot = 211 TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT; 212 213 @NonNull private final Object mLock = new Object(); 214 215 @NonNull private final PersistableBundleUtils.LockingReadWriteHelper mConfigDiskRwHelper; 216 217 @GuardedBy("mLock") 218 @NonNull 219 private final Map<IBinder, PolicyListenerBinderDeath> mRegisteredPolicyListeners = 220 new ArrayMap<>(); 221 222 @GuardedBy("mLock") 223 @NonNull 224 private final Map<IBinder, VcnStatusCallbackInfo> mRegisteredStatusCallbacks = new ArrayMap<>(); 225 226 @VisibleForTesting(visibility = Visibility.PRIVATE) VcnManagementService(@onNull Context context, @NonNull Dependencies deps)227 VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) { 228 mContext = 229 requireNonNull(context, "Missing context") 230 .createAttributionContext(CONTEXT_ATTRIBUTION_TAG); 231 mDeps = requireNonNull(deps, "Missing dependencies"); 232 233 mLooper = mDeps.getLooper(); 234 mHandler = new Handler(mLooper); 235 mNetworkProvider = new VcnNetworkProvider(mContext, mLooper); 236 mTelephonySubscriptionTrackerCb = new VcnSubscriptionTrackerCallback(); 237 mTelephonySubscriptionTracker = mDeps.newTelephonySubscriptionTracker( 238 mContext, mLooper, mTelephonySubscriptionTrackerCb); 239 240 mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE); 241 242 mVcnBroadcastReceiver = new VcnBroadcastReceiver(); 243 244 final IntentFilter intentFilter = new IntentFilter(); 245 intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 246 intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); 247 intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 248 intentFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); 249 intentFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); 250 intentFilter.addDataScheme("package"); 251 mContext.registerReceiver( 252 mVcnBroadcastReceiver, intentFilter, null /* broadcastPermission */, mHandler); 253 254 // Run on handler to ensure I/O does not block system server startup 255 mHandler.post(() -> { 256 PersistableBundle configBundle = null; 257 try { 258 configBundle = mConfigDiskRwHelper.readFromDisk(); 259 } catch (IOException e1) { 260 logErr("Failed to read configs from disk; retrying", e1); 261 262 // Retry immediately. The IOException may have been transient. 263 try { 264 configBundle = mConfigDiskRwHelper.readFromDisk(); 265 } catch (IOException e2) { 266 logWtf("Failed to read configs from disk", e2); 267 return; 268 } 269 } 270 271 if (configBundle != null) { 272 final Map<ParcelUuid, VcnConfig> configs = 273 PersistableBundleUtils.toMap( 274 configBundle, 275 PersistableBundleUtils::toParcelUuid, 276 VcnConfig::new); 277 278 synchronized (mLock) { 279 for (Entry<ParcelUuid, VcnConfig> entry : configs.entrySet()) { 280 // Ensure no new configs are overwritten; a carrier app may have added a new 281 // config. 282 if (!mConfigs.containsKey(entry.getKey())) { 283 mConfigs.put(entry.getKey(), entry.getValue()); 284 } 285 } 286 287 // Re-evaluate subscriptions, and start/stop VCNs. This starts with an empty 288 // snapshot, and therefore safe even before telephony subscriptions are loaded. 289 mTelephonySubscriptionTrackerCb.onNewSnapshot(mLastSnapshot); 290 } 291 } 292 }); 293 } 294 295 // Package-visibility for SystemServer to create instances. create(@onNull Context context)296 static VcnManagementService create(@NonNull Context context) { 297 return new VcnManagementService(context, new Dependencies()); 298 } 299 300 /** External dependencies used by VcnManagementService, for injection in tests */ 301 @VisibleForTesting(visibility = Visibility.PRIVATE) 302 public static class Dependencies { 303 private HandlerThread mHandlerThread; 304 305 /** Retrieves a looper for the VcnManagementService */ getLooper()306 public Looper getLooper() { 307 if (mHandlerThread == null) { 308 synchronized (this) { 309 if (mHandlerThread == null) { 310 mHandlerThread = new HandlerThread(TAG); 311 mHandlerThread.start(); 312 } 313 } 314 } 315 return mHandlerThread.getLooper(); 316 } 317 318 /** Creates a new VcnInstance using the provided configuration */ newTelephonySubscriptionTracker( @onNull Context context, @NonNull Looper looper, @NonNull TelephonySubscriptionTrackerCallback callback)319 public TelephonySubscriptionTracker newTelephonySubscriptionTracker( 320 @NonNull Context context, 321 @NonNull Looper looper, 322 @NonNull TelephonySubscriptionTrackerCallback callback) { 323 return new TelephonySubscriptionTracker(context, new Handler(looper), callback); 324 } 325 326 /** 327 * Retrieves the caller's UID 328 * 329 * <p>This call MUST be made before calling {@link Binder#clearCallingIdentity}, otherwise 330 * this will not work properly. 331 * 332 * @return 333 */ getBinderCallingUid()334 public int getBinderCallingUid() { 335 return Binder.getCallingUid(); 336 } 337 338 /** 339 * Creates and returns a new {@link PersistableBundle.LockingReadWriteHelper} 340 * 341 * @param path the file path to read/write from/to. 342 * @return the {@link PersistableBundleUtils.LockingReadWriteHelper} instance 343 */ 344 public PersistableBundleUtils.LockingReadWriteHelper newPersistableBundleLockingReadWriteHelper(@onNull String path)345 newPersistableBundleLockingReadWriteHelper(@NonNull String path) { 346 return new PersistableBundleUtils.LockingReadWriteHelper(path); 347 } 348 349 /** Creates a new VcnContext */ newVcnContext( @onNull Context context, @NonNull Looper looper, @NonNull VcnNetworkProvider vcnNetworkProvider, boolean isInTestMode)350 public VcnContext newVcnContext( 351 @NonNull Context context, 352 @NonNull Looper looper, 353 @NonNull VcnNetworkProvider vcnNetworkProvider, 354 boolean isInTestMode) { 355 return new VcnContext(context, looper, vcnNetworkProvider, isInTestMode); 356 } 357 358 /** Creates a new Vcn instance using the provided configuration */ newVcn( @onNull VcnContext vcnContext, @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnCallback vcnCallback)359 public Vcn newVcn( 360 @NonNull VcnContext vcnContext, 361 @NonNull ParcelUuid subscriptionGroup, 362 @NonNull VcnConfig config, 363 @NonNull TelephonySubscriptionSnapshot snapshot, 364 @NonNull VcnCallback vcnCallback) { 365 return new Vcn(vcnContext, subscriptionGroup, config, snapshot, vcnCallback); 366 } 367 368 /** Gets the subId indicated by the given {@link WifiInfo}. */ getSubIdForWifiInfo(@onNull WifiInfo wifiInfo)369 public int getSubIdForWifiInfo(@NonNull WifiInfo wifiInfo) { 370 return wifiInfo.getSubscriptionId(); 371 } 372 373 /** Creates a new LocationPermissionChecker for the provided Context. */ newLocationPermissionChecker(@onNull Context context)374 public LocationPermissionChecker newLocationPermissionChecker(@NonNull Context context) { 375 return new LocationPermissionChecker(context); 376 } 377 378 /** Gets transports that need to be marked as restricted by the VCN from CarrierConfig */ 379 @VisibleForTesting(visibility = Visibility.PRIVATE) getRestrictedTransportsFromCarrierConfig( ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot)380 public Set<Integer> getRestrictedTransportsFromCarrierConfig( 381 ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot) { 382 if (!Build.IS_ENG && !Build.IS_USERDEBUG) { 383 return RESTRICTED_TRANSPORTS_DEFAULT; 384 } 385 386 final PersistableBundleWrapper carrierConfig = 387 lastSnapshot.getCarrierConfigForSubGrp(subGrp); 388 if (carrierConfig == null) { 389 return RESTRICTED_TRANSPORTS_DEFAULT; 390 } 391 392 final int[] defaultValue = 393 RESTRICTED_TRANSPORTS_DEFAULT.stream().mapToInt(i -> i).toArray(); 394 final int[] restrictedTransportsArray = 395 carrierConfig.getIntArray( 396 VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, 397 defaultValue); 398 399 // Convert to a boxed set 400 final Set<Integer> restrictedTransports = new ArraySet<>(); 401 for (int transport : restrictedTransportsArray) { 402 restrictedTransports.add(transport); 403 } 404 return restrictedTransports; 405 } 406 407 /** Gets the transports that need to be marked as restricted by the VCN */ getRestrictedTransports( ParcelUuid subGrp, TelephonySubscriptionSnapshot lastSnapshot, VcnConfig vcnConfig)408 public Set<Integer> getRestrictedTransports( 409 ParcelUuid subGrp, 410 TelephonySubscriptionSnapshot lastSnapshot, 411 VcnConfig vcnConfig) { 412 final Set<Integer> restrictedTransports = new ArraySet<>(); 413 restrictedTransports.addAll(vcnConfig.getRestrictedUnderlyingNetworkTransports()); 414 415 // TODO: b/262269892 Remove the ability to configure restricted transports 416 // via CarrierConfig 417 restrictedTransports.addAll( 418 getRestrictedTransportsFromCarrierConfig(subGrp, lastSnapshot)); 419 420 return restrictedTransports; 421 } 422 } 423 424 /** Notifies the VcnManagementService that external dependencies can be set up. */ systemReady()425 public void systemReady() { 426 mNetworkProvider.register(); 427 mContext.getSystemService(ConnectivityManager.class) 428 .registerNetworkCallback( 429 new NetworkRequest.Builder().clearCapabilities().build(), 430 mTrackingNetworkCallback); 431 mTelephonySubscriptionTracker.register(); 432 } 433 enforcePrimaryUser()434 private void enforcePrimaryUser() { 435 final int uid = mDeps.getBinderCallingUid(); 436 if (uid == Process.SYSTEM_UID) { 437 throw new IllegalStateException( 438 "Calling identity was System Server. Was Binder calling identity cleared?"); 439 } 440 441 if (!UserHandle.getUserHandleForUid(uid).isSystem()) { 442 throw new SecurityException( 443 "VcnManagementService can only be used by callers running as the primary user"); 444 } 445 } 446 enforceCallingUserAndCarrierPrivilege( ParcelUuid subscriptionGroup, String pkgName)447 private void enforceCallingUserAndCarrierPrivilege( 448 ParcelUuid subscriptionGroup, String pkgName) { 449 // Only apps running in the primary (system) user are allowed to configure the VCN. This is 450 // in line with Telephony's behavior with regards to binding to a Carrier App provided 451 // CarrierConfigService. 452 enforcePrimaryUser(); 453 454 // TODO (b/172619301): Check based on events propagated from CarrierPrivilegesTracker 455 final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class); 456 final List<SubscriptionInfo> subscriptionInfos = new ArrayList<>(); 457 Binder.withCleanCallingIdentity( 458 () -> { 459 subscriptionInfos.addAll(subMgr.getSubscriptionsInGroup(subscriptionGroup)); 460 }); 461 462 for (SubscriptionInfo info : subscriptionInfos) { 463 final TelephonyManager telMgr = mContext.getSystemService(TelephonyManager.class) 464 .createForSubscriptionId(info.getSubscriptionId()); 465 466 // Check subscription is active first; much cheaper/faster check, and an app (currently) 467 // cannot be carrier privileged for inactive subscriptions. 468 if (subMgr.isValidSlotIndex(info.getSimSlotIndex()) 469 && telMgr.checkCarrierPrivilegesForPackage(pkgName) 470 == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) { 471 // TODO (b/173717728): Allow configuration for inactive, but manageable 472 // subscriptions. 473 // TODO (b/173718661): Check for whole subscription groups at a time. 474 return; 475 } 476 } 477 478 throw new SecurityException( 479 "Carrier privilege required for subscription group to set VCN Config"); 480 } 481 enforceManageTestNetworksForTestMode(@onNull VcnConfig vcnConfig)482 private void enforceManageTestNetworksForTestMode(@NonNull VcnConfig vcnConfig) { 483 if (vcnConfig.isTestModeProfile()) { 484 mContext.enforceCallingPermission( 485 android.Manifest.permission.MANAGE_TEST_NETWORKS, 486 "Test-mode require the MANAGE_TEST_NETWORKS permission"); 487 } 488 } 489 isActiveSubGroup( @onNull ParcelUuid subGrp, @NonNull TelephonySubscriptionSnapshot snapshot)490 private boolean isActiveSubGroup( 491 @NonNull ParcelUuid subGrp, @NonNull TelephonySubscriptionSnapshot snapshot) { 492 if (subGrp == null || snapshot == null) { 493 return false; 494 } 495 496 return Objects.equals(subGrp, snapshot.getActiveDataSubscriptionGroup()); 497 } 498 499 private class VcnBroadcastReceiver extends BroadcastReceiver { 500 @Override onReceive(Context context, Intent intent)501 public void onReceive(Context context, Intent intent) { 502 final String action = intent.getAction(); 503 504 switch (action) { 505 case Intent.ACTION_PACKAGE_ADDED: // Fallthrough 506 case Intent.ACTION_PACKAGE_REPLACED: // Fallthrough 507 case Intent.ACTION_PACKAGE_REMOVED: 508 // Reevaluate subscriptions 509 mTelephonySubscriptionTracker.handleSubscriptionsChanged(); 510 511 break; 512 case Intent.ACTION_PACKAGE_FULLY_REMOVED: 513 case Intent.ACTION_PACKAGE_DATA_CLEARED: 514 final String pkgName = intent.getData().getSchemeSpecificPart(); 515 516 if (pkgName == null || pkgName.isEmpty()) { 517 logWtf("Package name was empty or null for intent with action" + action); 518 return; 519 } 520 521 // Clear configs for the packages that had data cleared, or removed. 522 synchronized (mLock) { 523 final List<ParcelUuid> toRemove = new ArrayList<>(); 524 for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) { 525 if (pkgName.equals(entry.getValue().getProvisioningPackageName())) { 526 toRemove.add(entry.getKey()); 527 } 528 } 529 530 for (ParcelUuid subGrp : toRemove) { 531 stopAndClearVcnConfigInternalLocked(subGrp); 532 } 533 534 if (!toRemove.isEmpty()) { 535 writeConfigsToDiskLocked(); 536 } 537 } 538 539 break; 540 default: 541 Slog.wtf(TAG, "received unexpected intent: " + action); 542 } 543 } 544 } 545 546 private class VcnSubscriptionTrackerCallback implements TelephonySubscriptionTrackerCallback { 547 /** 548 * Handles subscription group changes, as notified by {@link TelephonySubscriptionTracker} 549 * 550 * <p>Start any unstarted VCN instances 551 * 552 * @hide 553 */ onNewSnapshot(@onNull TelephonySubscriptionSnapshot snapshot)554 public void onNewSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) { 555 // Startup VCN instances 556 synchronized (mLock) { 557 final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot; 558 mLastSnapshot = snapshot; 559 logInfo("new snapshot: " + mLastSnapshot); 560 561 // Start any VCN instances as necessary 562 for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) { 563 final ParcelUuid subGrp = entry.getKey(); 564 565 // TODO(b/193687515): Support multiple VCNs active at the same time 566 if (snapshot.packageHasPermissionsForSubscriptionGroup( 567 subGrp, entry.getValue().getProvisioningPackageName()) 568 && isActiveSubGroup(subGrp, snapshot)) { 569 if (!mVcns.containsKey(subGrp)) { 570 startVcnLocked(subGrp, entry.getValue()); 571 } 572 573 // Cancel any scheduled teardowns for active subscriptions 574 mHandler.removeCallbacksAndMessages(mVcns.get(subGrp)); 575 } 576 } 577 578 boolean needNotifyAllPolicyListeners = false; 579 // Schedule teardown of any VCN instances that have lost carrier privileges (after a 580 // delay) 581 for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) { 582 final ParcelUuid subGrp = entry.getKey(); 583 final VcnConfig config = mConfigs.get(subGrp); 584 585 final boolean isActiveSubGrp = isActiveSubGroup(subGrp, snapshot); 586 final boolean isValidActiveDataSubIdNotInVcnSubGrp = 587 isValidSubscriptionId(snapshot.getActiveDataSubscriptionId()) 588 && !isActiveSubGroup(subGrp, snapshot); 589 590 // TODO(b/193687515): Support multiple VCNs active at the same time 591 if (config == null 592 || !snapshot.packageHasPermissionsForSubscriptionGroup( 593 subGrp, config.getProvisioningPackageName()) 594 || !isActiveSubGrp) { 595 final ParcelUuid uuidToTeardown = subGrp; 596 final Vcn instanceToTeardown = entry.getValue(); 597 598 // TODO(b/193687515): Support multiple VCNs active at the same time 599 // If directly switching to a subscription not in the current group, 600 // teardown immediately to prevent other subscription's network from being 601 // outscored by the VCN. Otherwise, teardown after a delay to ensure that 602 // SIM profile switches do not trigger the VCN to cycle. 603 final long teardownDelayMs = 604 isValidActiveDataSubIdNotInVcnSubGrp 605 ? 0 606 : CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS; 607 mHandler.postDelayed(() -> { 608 synchronized (mLock) { 609 // Guard against case where this is run after a old instance was 610 // torn down, and a new instance was started. Verify to ensure 611 // correct instance is torn down. This could happen as a result of a 612 // Carrier App manually removing/adding a VcnConfig. 613 if (mVcns.get(uuidToTeardown) == instanceToTeardown) { 614 stopVcnLocked(uuidToTeardown); 615 616 // TODO(b/181789060): invoke asynchronously after Vcn notifies 617 // through VcnCallback 618 notifyAllPermissionedStatusCallbacksLocked( 619 uuidToTeardown, VCN_STATUS_CODE_INACTIVE); 620 } 621 } 622 }, instanceToTeardown, teardownDelayMs); 623 } else { 624 // If this VCN's status has not changed, update it with the new snapshot 625 entry.getValue().updateSubscriptionSnapshot(mLastSnapshot); 626 needNotifyAllPolicyListeners |= 627 !Objects.equals( 628 oldSnapshot.getCarrierConfigForSubGrp(subGrp), 629 mLastSnapshot.getCarrierConfigForSubGrp(subGrp)); 630 } 631 } 632 633 final Map<ParcelUuid, Set<Integer>> oldSubGrpMappings = 634 getSubGroupToSubIdMappings(oldSnapshot); 635 final Map<ParcelUuid, Set<Integer>> currSubGrpMappings = 636 getSubGroupToSubIdMappings(mLastSnapshot); 637 if (!currSubGrpMappings.equals(oldSubGrpMappings)) { 638 garbageCollectAndWriteVcnConfigsLocked(); 639 needNotifyAllPolicyListeners = true; 640 } 641 642 if (needNotifyAllPolicyListeners) { 643 notifyAllPolicyListenersLocked(); 644 } 645 } 646 } 647 } 648 649 @GuardedBy("mLock") getSubGroupToSubIdMappings( @onNull TelephonySubscriptionSnapshot snapshot)650 private Map<ParcelUuid, Set<Integer>> getSubGroupToSubIdMappings( 651 @NonNull TelephonySubscriptionSnapshot snapshot) { 652 final Map<ParcelUuid, Set<Integer>> subGrpMappings = new ArrayMap<>(); 653 for (ParcelUuid subGrp : mVcns.keySet()) { 654 subGrpMappings.put(subGrp, snapshot.getAllSubIdsInGroup(subGrp)); 655 } 656 return subGrpMappings; 657 } 658 659 @GuardedBy("mLock") stopVcnLocked(@onNull ParcelUuid uuidToTeardown)660 private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) { 661 logInfo("Stopping VCN config for subGrp: " + uuidToTeardown); 662 663 // Remove in 2 steps. Make sure teardownAsync is triggered before removing from the map. 664 final Vcn vcnToTeardown = mVcns.get(uuidToTeardown); 665 if (vcnToTeardown == null) { 666 return; 667 } 668 669 vcnToTeardown.teardownAsynchronously(); 670 mVcns.remove(uuidToTeardown); 671 672 // Now that the VCN is removed, notify all registered listeners to refresh their 673 // UnderlyingNetworkPolicy. 674 notifyAllPolicyListenersLocked(); 675 } 676 677 @GuardedBy("mLock") notifyAllPolicyListenersLocked()678 private void notifyAllPolicyListenersLocked() { 679 for (final PolicyListenerBinderDeath policyListener : mRegisteredPolicyListeners.values()) { 680 Binder.withCleanCallingIdentity(() -> { 681 try { 682 policyListener.mListener.onPolicyChanged(); 683 } catch (RemoteException e) { 684 logDbg("VcnStatusCallback threw on VCN status change", e); 685 } 686 }); 687 } 688 } 689 690 @GuardedBy("mLock") notifyAllPermissionedStatusCallbacksLocked( @onNull ParcelUuid subGroup, @VcnStatusCode int statusCode)691 private void notifyAllPermissionedStatusCallbacksLocked( 692 @NonNull ParcelUuid subGroup, @VcnStatusCode int statusCode) { 693 for (final VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { 694 if (isCallbackPermissioned(cbInfo, subGroup)) { 695 Binder.withCleanCallingIdentity(() -> { 696 try { 697 cbInfo.mCallback.onVcnStatusChanged(statusCode); 698 } catch (RemoteException e) { 699 logDbg("VcnStatusCallback threw on VCN status change", e); 700 } 701 }); 702 } 703 } 704 } 705 706 @GuardedBy("mLock") startVcnLocked(@onNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config)707 private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) { 708 logInfo("Starting VCN config for subGrp: " + subscriptionGroup); 709 710 // TODO(b/193687515): Support multiple VCNs active at the same time 711 if (!mVcns.isEmpty()) { 712 // Only one VCN supported at a time; teardown all others before starting new one 713 for (ParcelUuid uuidToTeardown : mVcns.keySet()) { 714 stopVcnLocked(uuidToTeardown); 715 } 716 } 717 718 final VcnCallbackImpl vcnCallback = new VcnCallbackImpl(subscriptionGroup); 719 720 final VcnContext vcnContext = 721 mDeps.newVcnContext( 722 mContext, mLooper, mNetworkProvider, config.isTestModeProfile()); 723 final Vcn newInstance = 724 mDeps.newVcn(vcnContext, subscriptionGroup, config, mLastSnapshot, vcnCallback); 725 mVcns.put(subscriptionGroup, newInstance); 726 727 // Now that a new VCN has started, notify all registered listeners to refresh their 728 // UnderlyingNetworkPolicy. 729 notifyAllPolicyListenersLocked(); 730 731 // TODO(b/181789060): invoke asynchronously after Vcn notifies through VcnCallback 732 notifyAllPermissionedStatusCallbacksLocked(subscriptionGroup, VCN_STATUS_CODE_ACTIVE); 733 } 734 735 @GuardedBy("mLock") startOrUpdateVcnLocked( @onNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config)736 private void startOrUpdateVcnLocked( 737 @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) { 738 logDbg("Starting or updating VCN config for subGrp: " + subscriptionGroup); 739 740 if (mVcns.containsKey(subscriptionGroup)) { 741 final Vcn vcn = mVcns.get(subscriptionGroup); 742 vcn.updateConfig(config); 743 notifyAllPolicyListenersLocked(); 744 } else { 745 // TODO(b/193687515): Support multiple VCNs active at the same time 746 if (isActiveSubGroup(subscriptionGroup, mLastSnapshot)) { 747 startVcnLocked(subscriptionGroup, config); 748 } 749 } 750 } 751 752 /** 753 * Sets a VCN config for a given subscription group. 754 * 755 * <p>Implements the IVcnManagementService Binder interface. 756 */ 757 @Override setVcnConfig( @onNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull String opPkgName)758 public void setVcnConfig( 759 @NonNull ParcelUuid subscriptionGroup, 760 @NonNull VcnConfig config, 761 @NonNull String opPkgName) { 762 requireNonNull(subscriptionGroup, "subscriptionGroup was null"); 763 requireNonNull(config, "config was null"); 764 requireNonNull(opPkgName, "opPkgName was null"); 765 if (!config.getProvisioningPackageName().equals(opPkgName)) { 766 throw new IllegalArgumentException("Mismatched caller and VcnConfig creator"); 767 } 768 logInfo("VCN config updated for subGrp: " + subscriptionGroup); 769 770 mContext.getSystemService(AppOpsManager.class) 771 .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName()); 772 enforceManageTestNetworksForTestMode(config); 773 enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName); 774 775 Binder.withCleanCallingIdentity(() -> { 776 synchronized (mLock) { 777 mConfigs.put(subscriptionGroup, config); 778 startOrUpdateVcnLocked(subscriptionGroup, config); 779 780 writeConfigsToDiskLocked(); 781 } 782 }); 783 } 784 enforceCarrierPrivilegeOrProvisioningPackage( @onNull ParcelUuid subscriptionGroup, @NonNull String pkg)785 private void enforceCarrierPrivilegeOrProvisioningPackage( 786 @NonNull ParcelUuid subscriptionGroup, @NonNull String pkg) { 787 // Only apps running in the primary (system) user are allowed to configure the VCN. This is 788 // in line with Telephony's behavior with regards to binding to a Carrier App provided 789 // CarrierConfigService. 790 enforcePrimaryUser(); 791 792 if (isProvisioningPackageForConfig(subscriptionGroup, pkg)) { 793 return; 794 } 795 796 // Must NOT be called from cleared binder identity, since this checks user calling identity 797 enforceCallingUserAndCarrierPrivilege(subscriptionGroup, pkg); 798 } 799 isProvisioningPackageForConfig( @onNull ParcelUuid subscriptionGroup, @NonNull String pkg)800 private boolean isProvisioningPackageForConfig( 801 @NonNull ParcelUuid subscriptionGroup, @NonNull String pkg) { 802 // Try-finally to return early if matching owned subscription found. 803 final long identity = Binder.clearCallingIdentity(); 804 try { 805 synchronized (mLock) { 806 final VcnConfig config = mConfigs.get(subscriptionGroup); 807 if (config != null && pkg.equals(config.getProvisioningPackageName())) { 808 return true; 809 } 810 } 811 } finally { 812 Binder.restoreCallingIdentity(identity); 813 } 814 815 return false; 816 } 817 818 /** 819 * Clears the VcnManagementService for a given subscription group. 820 * 821 * <p>Implements the IVcnManagementService Binder interface. 822 */ 823 @Override clearVcnConfig(@onNull ParcelUuid subscriptionGroup, @NonNull String opPkgName)824 public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull String opPkgName) { 825 requireNonNull(subscriptionGroup, "subscriptionGroup was null"); 826 requireNonNull(opPkgName, "opPkgName was null"); 827 logInfo("VCN config cleared for subGrp: " + subscriptionGroup); 828 829 mContext.getSystemService(AppOpsManager.class) 830 .checkPackage(mDeps.getBinderCallingUid(), opPkgName); 831 enforceCarrierPrivilegeOrProvisioningPackage(subscriptionGroup, opPkgName); 832 833 Binder.withCleanCallingIdentity(() -> { 834 synchronized (mLock) { 835 stopAndClearVcnConfigInternalLocked(subscriptionGroup); 836 writeConfigsToDiskLocked(); 837 } 838 }); 839 } 840 stopAndClearVcnConfigInternalLocked(@onNull ParcelUuid subscriptionGroup)841 private void stopAndClearVcnConfigInternalLocked(@NonNull ParcelUuid subscriptionGroup) { 842 mConfigs.remove(subscriptionGroup); 843 final boolean vcnExists = mVcns.containsKey(subscriptionGroup); 844 845 stopVcnLocked(subscriptionGroup); 846 847 if (vcnExists) { 848 // TODO(b/181789060): invoke asynchronously after Vcn notifies through 849 // VcnCallback 850 notifyAllPermissionedStatusCallbacksLocked( 851 subscriptionGroup, VCN_STATUS_CODE_NOT_CONFIGURED); 852 } 853 } 854 garbageCollectAndWriteVcnConfigsLocked()855 private void garbageCollectAndWriteVcnConfigsLocked() { 856 final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class); 857 858 boolean shouldWrite = false; 859 860 final Iterator<ParcelUuid> configsIterator = mConfigs.keySet().iterator(); 861 while (configsIterator.hasNext()) { 862 final ParcelUuid subGrp = configsIterator.next(); 863 864 final List<SubscriptionInfo> subscriptions = subMgr.getSubscriptionsInGroup(subGrp); 865 if (subscriptions == null || subscriptions.isEmpty()) { 866 // Trim subGrps with no more subscriptions; must have moved to another subGrp 867 configsIterator.remove(); 868 shouldWrite = true; 869 } 870 } 871 872 if (shouldWrite) { 873 writeConfigsToDiskLocked(); 874 } 875 } 876 877 /** 878 * Retrieves the list of subscription groups with configured VcnConfigs 879 * 880 * <p>Limited to subscription groups for which the caller had configured. 881 * 882 * <p>Implements the IVcnManagementService Binder interface. 883 */ 884 @Override 885 @NonNull getConfiguredSubscriptionGroups(@onNull String opPkgName)886 public List<ParcelUuid> getConfiguredSubscriptionGroups(@NonNull String opPkgName) { 887 requireNonNull(opPkgName, "opPkgName was null"); 888 889 mContext.getSystemService(AppOpsManager.class) 890 .checkPackage(mDeps.getBinderCallingUid(), opPkgName); 891 enforcePrimaryUser(); 892 893 final List<ParcelUuid> result = new ArrayList<>(); 894 synchronized (mLock) { 895 for (ParcelUuid subGrp : mConfigs.keySet()) { 896 if (mLastSnapshot.packageHasPermissionsForSubscriptionGroup(subGrp, opPkgName) 897 || isProvisioningPackageForConfig(subGrp, opPkgName)) { 898 result.add(subGrp); 899 } 900 } 901 } 902 903 return result; 904 } 905 906 @GuardedBy("mLock") writeConfigsToDiskLocked()907 private void writeConfigsToDiskLocked() { 908 try { 909 PersistableBundle bundle = 910 PersistableBundleUtils.fromMap( 911 mConfigs, 912 PersistableBundleUtils::fromParcelUuid, 913 VcnConfig::toPersistableBundle); 914 mConfigDiskRwHelper.writeToDisk(bundle); 915 } catch (IOException e) { 916 logErr("Failed to save configs to disk", e); 917 throw new ServiceSpecificException(0, "Failed to save configs"); 918 } 919 } 920 921 /** Get current configuration list for testing purposes */ 922 @VisibleForTesting(visibility = Visibility.PRIVATE) getConfigs()923 Map<ParcelUuid, VcnConfig> getConfigs() { 924 synchronized (mLock) { 925 return Collections.unmodifiableMap(mConfigs); 926 } 927 } 928 929 /** Get current VCNs for testing purposes */ 930 @VisibleForTesting(visibility = Visibility.PRIVATE) getAllVcns()931 public Map<ParcelUuid, Vcn> getAllVcns() { 932 synchronized (mLock) { 933 return Collections.unmodifiableMap(mVcns); 934 } 935 } 936 937 /** Get current VcnStatusCallbacks for testing purposes. */ 938 @VisibleForTesting(visibility = Visibility.PRIVATE) getAllStatusCallbacks()939 public Map<IBinder, VcnStatusCallbackInfo> getAllStatusCallbacks() { 940 synchronized (mLock) { 941 return Collections.unmodifiableMap(mRegisteredStatusCallbacks); 942 } 943 } 944 945 /** Binder death recipient used to remove a registered policy listener. */ 946 private class PolicyListenerBinderDeath implements Binder.DeathRecipient { 947 @NonNull private final IVcnUnderlyingNetworkPolicyListener mListener; 948 PolicyListenerBinderDeath(@onNull IVcnUnderlyingNetworkPolicyListener listener)949 PolicyListenerBinderDeath(@NonNull IVcnUnderlyingNetworkPolicyListener listener) { 950 mListener = listener; 951 } 952 953 @Override binderDied()954 public void binderDied() { 955 Log.e(TAG, "app died without removing VcnUnderlyingNetworkPolicyListener"); 956 removeVcnUnderlyingNetworkPolicyListener(mListener); 957 } 958 } 959 960 /** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */ 961 @Override addVcnUnderlyingNetworkPolicyListener( @onNull IVcnUnderlyingNetworkPolicyListener listener)962 public void addVcnUnderlyingNetworkPolicyListener( 963 @NonNull IVcnUnderlyingNetworkPolicyListener listener) { 964 requireNonNull(listener, "listener was null"); 965 966 PermissionUtils.enforceAnyPermissionOf( 967 mContext, 968 android.Manifest.permission.NETWORK_FACTORY, 969 android.Manifest.permission.MANAGE_TEST_NETWORKS); 970 971 Binder.withCleanCallingIdentity(() -> { 972 PolicyListenerBinderDeath listenerBinderDeath = new PolicyListenerBinderDeath(listener); 973 974 synchronized (mLock) { 975 mRegisteredPolicyListeners.put(listener.asBinder(), listenerBinderDeath); 976 977 try { 978 listener.asBinder().linkToDeath(listenerBinderDeath, 0 /* flags */); 979 } catch (RemoteException e) { 980 // Remote binder already died - cleanup registered Listener 981 listenerBinderDeath.binderDied(); 982 } 983 } 984 }); 985 } 986 987 /** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */ 988 @Override removeVcnUnderlyingNetworkPolicyListener( @onNull IVcnUnderlyingNetworkPolicyListener listener)989 public void removeVcnUnderlyingNetworkPolicyListener( 990 @NonNull IVcnUnderlyingNetworkPolicyListener listener) { 991 requireNonNull(listener, "listener was null"); 992 993 PermissionUtils.enforceAnyPermissionOf( 994 mContext, 995 android.Manifest.permission.NETWORK_FACTORY, 996 android.Manifest.permission.MANAGE_TEST_NETWORKS); 997 998 Binder.withCleanCallingIdentity(() -> { 999 synchronized (mLock) { 1000 PolicyListenerBinderDeath listenerBinderDeath = 1001 mRegisteredPolicyListeners.remove(listener.asBinder()); 1002 1003 if (listenerBinderDeath != null) { 1004 listener.asBinder().unlinkToDeath(listenerBinderDeath, 0 /* flags */); 1005 } 1006 } 1007 }); 1008 } 1009 getSubGroupForNetworkCapabilities( @onNull NetworkCapabilities networkCapabilities)1010 private ParcelUuid getSubGroupForNetworkCapabilities( 1011 @NonNull NetworkCapabilities networkCapabilities) { 1012 ParcelUuid subGrp = null; 1013 final TelephonySubscriptionSnapshot snapshot; 1014 1015 // Always access mLastSnapshot under lock. Technically this can be treated as a volatile 1016 // but for consistency and safety, always access under lock. 1017 synchronized (mLock) { 1018 snapshot = mLastSnapshot; 1019 } 1020 1021 // If multiple subscription IDs exist, they MUST all point to the same subscription 1022 // group. Otherwise undefined behavior may occur. 1023 for (int subId : networkCapabilities.getSubscriptionIds()) { 1024 // Verify that all subscriptions point to the same group 1025 if (subGrp != null && !subGrp.equals(snapshot.getGroupForSubId(subId))) { 1026 logWtf("Got multiple subscription groups for a single network"); 1027 } 1028 1029 subGrp = snapshot.getGroupForSubId(subId); 1030 } 1031 1032 return subGrp; 1033 } 1034 1035 /** 1036 * Gets the UnderlyingNetworkPolicy as determined by the provided NetworkCapabilities and 1037 * LinkProperties. 1038 */ 1039 @NonNull 1040 @Override getUnderlyingNetworkPolicy( @onNull NetworkCapabilities networkCapabilities, @NonNull LinkProperties linkProperties)1041 public VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy( 1042 @NonNull NetworkCapabilities networkCapabilities, 1043 @NonNull LinkProperties linkProperties) { 1044 requireNonNull(networkCapabilities, "networkCapabilities was null"); 1045 requireNonNull(linkProperties, "linkProperties was null"); 1046 1047 PermissionUtils.enforceAnyPermissionOf( 1048 mContext, 1049 android.Manifest.permission.NETWORK_FACTORY, 1050 android.Manifest.permission.MANAGE_TEST_NETWORKS); 1051 1052 final boolean isUsingManageTestNetworks = 1053 mContext.checkCallingOrSelfPermission(android.Manifest.permission.NETWORK_FACTORY) 1054 != PackageManager.PERMISSION_GRANTED; 1055 1056 if (isUsingManageTestNetworks && !networkCapabilities.hasTransport(TRANSPORT_TEST)) { 1057 throw new IllegalStateException( 1058 "NetworkCapabilities must be for Test Network if using permission" 1059 + " MANAGE_TEST_NETWORKS"); 1060 } 1061 1062 return Binder.withCleanCallingIdentity(() -> { 1063 // Defensive copy in case this call is in-process and the given NetworkCapabilities 1064 // mutates 1065 final NetworkCapabilities ncCopy = new NetworkCapabilities(networkCapabilities); 1066 1067 final ParcelUuid subGrp = getSubGroupForNetworkCapabilities(ncCopy); 1068 boolean isVcnManagedNetwork = false; 1069 boolean isRestricted = false; 1070 synchronized (mLock) { 1071 final Vcn vcn = mVcns.get(subGrp); 1072 final VcnConfig vcnConfig = mConfigs.get(subGrp); 1073 if (vcn != null) { 1074 if (vcnConfig == null) { 1075 // TODO: b/284381334 Investigate for the root cause of this issue 1076 // and handle it properly 1077 logWtf("Vcn instance exists but VcnConfig does not for " + subGrp); 1078 } 1079 1080 if (vcn.getStatus() == VCN_STATUS_CODE_ACTIVE) { 1081 isVcnManagedNetwork = true; 1082 } 1083 1084 final Set<Integer> restrictedTransports = mDeps.getRestrictedTransports( 1085 subGrp, mLastSnapshot, vcnConfig); 1086 for (int restrictedTransport : restrictedTransports) { 1087 if (ncCopy.hasTransport(restrictedTransport)) { 1088 if (restrictedTransport == TRANSPORT_CELLULAR 1089 || restrictedTransport == TRANSPORT_TEST) { 1090 // For cell or test network, only mark it as restricted when 1091 // the VCN is in active mode. 1092 isRestricted |= (vcn.getStatus() == VCN_STATUS_CODE_ACTIVE); 1093 } else { 1094 isRestricted = true; 1095 break; 1096 } 1097 } 1098 } 1099 } 1100 } 1101 1102 final NetworkCapabilities.Builder ncBuilder = new NetworkCapabilities.Builder(ncCopy); 1103 1104 if (isVcnManagedNetwork) { 1105 ncBuilder.removeCapability( 1106 NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); 1107 } else { 1108 ncBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); 1109 } 1110 1111 if (isRestricted) { 1112 ncBuilder.removeCapability( 1113 NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); 1114 } 1115 1116 final NetworkCapabilities result = ncBuilder.build(); 1117 final VcnUnderlyingNetworkPolicy policy = new VcnUnderlyingNetworkPolicy( 1118 mTrackingNetworkCallback 1119 .requiresRestartForImmutableCapabilityChanges(result, linkProperties), 1120 result); 1121 1122 logVdbg("getUnderlyingNetworkPolicy() called for caps: " + networkCapabilities 1123 + "; and lp: " + linkProperties + "; result = " + policy); 1124 return policy; 1125 }); 1126 } 1127 1128 /** Binder death recipient used to remove registered VcnStatusCallbacks. */ 1129 @VisibleForTesting(visibility = Visibility.PRIVATE) 1130 class VcnStatusCallbackInfo implements Binder.DeathRecipient { 1131 @NonNull final ParcelUuid mSubGroup; 1132 @NonNull final IVcnStatusCallback mCallback; 1133 @NonNull final String mPkgName; 1134 final int mUid; 1135 VcnStatusCallbackInfo( @onNull ParcelUuid subGroup, @NonNull IVcnStatusCallback callback, @NonNull String pkgName, int uid)1136 private VcnStatusCallbackInfo( 1137 @NonNull ParcelUuid subGroup, 1138 @NonNull IVcnStatusCallback callback, 1139 @NonNull String pkgName, 1140 int uid) { 1141 mSubGroup = subGroup; 1142 mCallback = callback; 1143 mPkgName = pkgName; 1144 mUid = uid; 1145 } 1146 1147 @Override binderDied()1148 public void binderDied() { 1149 Log.e(TAG, "app died without unregistering VcnStatusCallback"); 1150 unregisterVcnStatusCallback(mCallback); 1151 } 1152 } 1153 isCallbackPermissioned( @onNull VcnStatusCallbackInfo cbInfo, @NonNull ParcelUuid subgroup)1154 private boolean isCallbackPermissioned( 1155 @NonNull VcnStatusCallbackInfo cbInfo, @NonNull ParcelUuid subgroup) { 1156 if (!subgroup.equals(cbInfo.mSubGroup)) { 1157 return false; 1158 } 1159 1160 if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup(subgroup, cbInfo.mPkgName)) { 1161 return false; 1162 } 1163 1164 return true; 1165 } 1166 1167 /** Registers the provided callback for receiving VCN status updates. */ 1168 @Override registerVcnStatusCallback( @onNull ParcelUuid subGroup, @NonNull IVcnStatusCallback callback, @NonNull String opPkgName)1169 public void registerVcnStatusCallback( 1170 @NonNull ParcelUuid subGroup, 1171 @NonNull IVcnStatusCallback callback, 1172 @NonNull String opPkgName) { 1173 final int callingUid = mDeps.getBinderCallingUid(); 1174 final long identity = Binder.clearCallingIdentity(); 1175 try { 1176 requireNonNull(subGroup, "subGroup must not be null"); 1177 requireNonNull(callback, "callback must not be null"); 1178 requireNonNull(opPkgName, "opPkgName must not be null"); 1179 1180 mContext.getSystemService(AppOpsManager.class).checkPackage(callingUid, opPkgName); 1181 1182 final IBinder cbBinder = callback.asBinder(); 1183 final VcnStatusCallbackInfo cbInfo = 1184 new VcnStatusCallbackInfo(subGroup, callback, opPkgName, callingUid); 1185 1186 try { 1187 cbBinder.linkToDeath(cbInfo, 0 /* flags */); 1188 } catch (RemoteException e) { 1189 // Remote binder already died - don't add to mRegisteredStatusCallbacks and exit 1190 return; 1191 } 1192 1193 synchronized (mLock) { 1194 if (mRegisteredStatusCallbacks.containsKey(cbBinder)) { 1195 throw new IllegalStateException( 1196 "Attempting to register a callback that is already in use"); 1197 } 1198 1199 mRegisteredStatusCallbacks.put(cbBinder, cbInfo); 1200 1201 // now that callback is registered, send it the VCN's current status 1202 final VcnConfig vcnConfig = mConfigs.get(subGroup); 1203 final Vcn vcn = mVcns.get(subGroup); 1204 final int vcnStatus = 1205 vcn == null ? VCN_STATUS_CODE_NOT_CONFIGURED : vcn.getStatus(); 1206 final int resultStatus; 1207 if (vcnConfig == null || !isCallbackPermissioned(cbInfo, subGroup)) { 1208 resultStatus = VCN_STATUS_CODE_NOT_CONFIGURED; 1209 } else if (vcn == null) { 1210 resultStatus = VCN_STATUS_CODE_INACTIVE; 1211 } else if (vcnStatus == VCN_STATUS_CODE_ACTIVE 1212 || vcnStatus == VCN_STATUS_CODE_SAFE_MODE) { 1213 resultStatus = vcnStatus; 1214 } else { 1215 logWtf("Unknown VCN status: " + vcnStatus); 1216 resultStatus = VCN_STATUS_CODE_NOT_CONFIGURED; 1217 } 1218 1219 try { 1220 cbInfo.mCallback.onVcnStatusChanged(resultStatus); 1221 } catch (RemoteException e) { 1222 logDbg("VcnStatusCallback threw on VCN status change", e); 1223 } 1224 } 1225 } finally { 1226 Binder.restoreCallingIdentity(identity); 1227 } 1228 } 1229 1230 /** Unregisters the provided callback from receiving future VCN status updates. */ 1231 @Override unregisterVcnStatusCallback(@onNull IVcnStatusCallback callback)1232 public void unregisterVcnStatusCallback(@NonNull IVcnStatusCallback callback) { 1233 final long identity = Binder.clearCallingIdentity(); 1234 try { 1235 requireNonNull(callback, "callback must not be null"); 1236 1237 final IBinder cbBinder = callback.asBinder(); 1238 synchronized (mLock) { 1239 VcnStatusCallbackInfo cbInfo = mRegisteredStatusCallbacks.remove(cbBinder); 1240 1241 if (cbInfo != null) { 1242 cbBinder.unlinkToDeath(cbInfo, 0 /* flags */); 1243 } 1244 } 1245 } finally { 1246 Binder.restoreCallingIdentity(identity); 1247 } 1248 } 1249 1250 @VisibleForTesting(visibility = Visibility.PRIVATE) setLastSnapshot(@onNull TelephonySubscriptionSnapshot snapshot)1251 void setLastSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) { 1252 mLastSnapshot = Objects.requireNonNull(snapshot); 1253 } 1254 logVdbg(String msg)1255 private void logVdbg(String msg) { 1256 if (VDBG) { 1257 Slog.v(TAG, msg); 1258 } 1259 } 1260 logDbg(String msg)1261 private void logDbg(String msg) { 1262 Slog.d(TAG, msg); 1263 } 1264 logDbg(String msg, Throwable tr)1265 private void logDbg(String msg, Throwable tr) { 1266 Slog.d(TAG, msg, tr); 1267 } 1268 logInfo(String msg)1269 private void logInfo(String msg) { 1270 Slog.i(TAG, msg); 1271 LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg); 1272 } 1273 logInfo(String msg, Throwable tr)1274 private void logInfo(String msg, Throwable tr) { 1275 Slog.i(TAG, msg, tr); 1276 LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg + tr); 1277 } 1278 logErr(String msg)1279 private void logErr(String msg) { 1280 Slog.e(TAG, msg); 1281 LOCAL_LOG.log("[ERR] [" + TAG + "] " + msg); 1282 } 1283 logErr(String msg, Throwable tr)1284 private void logErr(String msg, Throwable tr) { 1285 Slog.e(TAG, msg, tr); 1286 LOCAL_LOG.log("[ERR ] [" + TAG + "] " + msg + tr); 1287 } 1288 logWtf(String msg)1289 private void logWtf(String msg) { 1290 Slog.wtf(TAG, msg); 1291 LOCAL_LOG.log("[WTF] [" + TAG + "] " + msg); 1292 } 1293 logWtf(String msg, Throwable tr)1294 private void logWtf(String msg, Throwable tr) { 1295 Slog.wtf(TAG, msg, tr); 1296 LOCAL_LOG.log("[WTF ] [" + TAG + "] " + msg + tr); 1297 } 1298 1299 /** 1300 * Dumps the state of the VcnManagementService for logging and debugging purposes. 1301 * 1302 * <p>PII and credentials MUST NEVER be dumped here. 1303 */ 1304 @Override dump(FileDescriptor fd, PrintWriter writer, String[] args)1305 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 1306 mContext.enforceCallingOrSelfPermission(DUMP, TAG); 1307 1308 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "| "); 1309 1310 // Post to handler thread to prevent ConcurrentModificationExceptions, and avoid lock-hell. 1311 mHandler.runWithScissors(() -> { 1312 mNetworkProvider.dump(pw); 1313 pw.println(); 1314 1315 mTrackingNetworkCallback.dump(pw); 1316 pw.println(); 1317 1318 synchronized (mLock) { 1319 mLastSnapshot.dump(pw); 1320 pw.println(); 1321 1322 pw.println("mConfigs:"); 1323 pw.increaseIndent(); 1324 for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) { 1325 pw.println(entry.getKey() + ": " 1326 + entry.getValue().getProvisioningPackageName()); 1327 } 1328 pw.decreaseIndent(); 1329 pw.println(); 1330 1331 pw.println("mVcns:"); 1332 pw.increaseIndent(); 1333 for (Vcn vcn : mVcns.values()) { 1334 vcn.dump(pw); 1335 } 1336 pw.decreaseIndent(); 1337 pw.println(); 1338 } 1339 1340 pw.println("Local log:"); 1341 pw.increaseIndent(); 1342 LOCAL_LOG.dump(pw); 1343 pw.decreaseIndent(); 1344 pw.println(); 1345 }, DUMP_TIMEOUT_MILLIS); 1346 } 1347 1348 // TODO(b/180452282): Make name more generic and implement directly with VcnManagementService 1349 /** Callback for Vcn signals sent up to VcnManagementService. */ 1350 public interface VcnCallback { 1351 /** Called by a Vcn to signal that its safe mode status has changed. */ onSafeModeStatusChanged(boolean isInSafeMode)1352 void onSafeModeStatusChanged(boolean isInSafeMode); 1353 1354 /** Called by a Vcn to signal that an error occurred. */ onGatewayConnectionError( @onNull String gatewayConnectionName, @VcnErrorCode int errorCode, @Nullable String exceptionClass, @Nullable String exceptionMessage)1355 void onGatewayConnectionError( 1356 @NonNull String gatewayConnectionName, 1357 @VcnErrorCode int errorCode, 1358 @Nullable String exceptionClass, 1359 @Nullable String exceptionMessage); 1360 } 1361 1362 /** 1363 * TrackingNetworkCallback tracks all active networks 1364 * 1365 * <p>This is used to ensure that no underlying networks have immutable capabilities changed 1366 * without requiring a Network restart. 1367 */ 1368 private class TrackingNetworkCallback extends ConnectivityManager.NetworkCallback { 1369 private final Object mLockObject = new Object(); 1370 private final Map<Network, NetworkCapabilities> mCaps = new ArrayMap<>(); 1371 private final Map<Network, LinkProperties> mLinkProperties = new ArrayMap<>(); 1372 1373 @Override onCapabilitiesChanged(Network network, NetworkCapabilities caps)1374 public void onCapabilitiesChanged(Network network, NetworkCapabilities caps) { 1375 synchronized (mLockObject) { 1376 mCaps.put(network, caps); 1377 } 1378 } 1379 1380 @Override onLinkPropertiesChanged(Network network, LinkProperties lp)1381 public void onLinkPropertiesChanged(Network network, LinkProperties lp) { 1382 synchronized (mLockObject) { 1383 mLinkProperties.put(network, lp); 1384 } 1385 } 1386 1387 @Override onLost(Network network)1388 public void onLost(Network network) { 1389 synchronized (mLockObject) { 1390 mCaps.remove(network); 1391 mLinkProperties.remove(network); 1392 } 1393 } 1394 getNonTestTransportTypes(NetworkCapabilities caps)1395 private Set<Integer> getNonTestTransportTypes(NetworkCapabilities caps) { 1396 final Set<Integer> transportTypes = new ArraySet<>(); 1397 for (int t : caps.getTransportTypes()) { 1398 transportTypes.add(t); 1399 } 1400 return transportTypes; 1401 } 1402 hasSameTransportsAndCapabilities( NetworkCapabilities caps, NetworkCapabilities capsOther)1403 private boolean hasSameTransportsAndCapabilities( 1404 NetworkCapabilities caps, NetworkCapabilities capsOther) { 1405 if (!Objects.equals( 1406 getNonTestTransportTypes(caps), getNonTestTransportTypes(capsOther))) { 1407 return false; 1408 } 1409 1410 for (int capability : ALLOWED_CAPABILITIES) { 1411 if (caps.hasCapability(capability) != capsOther.hasCapability(capability)) { 1412 return false; 1413 } 1414 } 1415 return true; 1416 } 1417 requiresRestartForImmutableCapabilityChanges( NetworkCapabilities caps, LinkProperties lp)1418 private boolean requiresRestartForImmutableCapabilityChanges( 1419 NetworkCapabilities caps, LinkProperties lp) { 1420 if (caps.getSubscriptionIds() == null) { 1421 return false; 1422 } 1423 1424 synchronized (mLockObject) { 1425 // Search for an existing network (using interfce names) 1426 // TODO: Get network from NetworkFactory (if exists) for this match. 1427 for (Entry<Network, LinkProperties> lpEntry : mLinkProperties.entrySet()) { 1428 if (lp.getInterfaceName() != null 1429 && !lp.getInterfaceName().isEmpty() 1430 && Objects.equals( 1431 lp.getInterfaceName(), lpEntry.getValue().getInterfaceName())) { 1432 return mCaps.get(lpEntry.getKey()) 1433 .hasCapability(NET_CAPABILITY_NOT_RESTRICTED) 1434 != caps.hasCapability(NET_CAPABILITY_NOT_RESTRICTED); 1435 } 1436 } 1437 } 1438 1439 // If no network found, by definition does not need restart. 1440 return false; 1441 } 1442 1443 /** Dumps the state of this snapshot for logging and debugging purposes. */ dump(IndentingPrintWriter pw)1444 public void dump(IndentingPrintWriter pw) { 1445 pw.println("TrackingNetworkCallback:"); 1446 pw.increaseIndent(); 1447 1448 pw.println("mCaps:"); 1449 pw.increaseIndent(); 1450 synchronized (mCaps) { 1451 for (Entry<Network, NetworkCapabilities> entry : mCaps.entrySet()) { 1452 pw.println(entry.getKey() + ": " + entry.getValue()); 1453 } 1454 } 1455 pw.decreaseIndent(); 1456 pw.println(); 1457 1458 pw.decreaseIndent(); 1459 } 1460 } 1461 1462 /** VcnCallbackImpl for Vcn signals sent up to VcnManagementService. */ 1463 private class VcnCallbackImpl implements VcnCallback { 1464 @NonNull private final ParcelUuid mSubGroup; 1465 VcnCallbackImpl(@onNull final ParcelUuid subGroup)1466 private VcnCallbackImpl(@NonNull final ParcelUuid subGroup) { 1467 mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup"); 1468 } 1469 1470 @Override onSafeModeStatusChanged(boolean isInSafeMode)1471 public void onSafeModeStatusChanged(boolean isInSafeMode) { 1472 synchronized (mLock) { 1473 // Ignore if this subscription group doesn't exist anymore 1474 if (!mVcns.containsKey(mSubGroup)) { 1475 return; 1476 } 1477 1478 final int status = 1479 isInSafeMode ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE; 1480 1481 notifyAllPolicyListenersLocked(); 1482 notifyAllPermissionedStatusCallbacksLocked(mSubGroup, status); 1483 } 1484 } 1485 1486 @Override onGatewayConnectionError( @onNull String gatewayConnectionName, @VcnErrorCode int errorCode, @Nullable String exceptionClass, @Nullable String exceptionMessage)1487 public void onGatewayConnectionError( 1488 @NonNull String gatewayConnectionName, 1489 @VcnErrorCode int errorCode, 1490 @Nullable String exceptionClass, 1491 @Nullable String exceptionMessage) { 1492 synchronized (mLock) { 1493 // Ignore if this subscription group doesn't exist anymore 1494 if (!mVcns.containsKey(mSubGroup)) { 1495 return; 1496 } 1497 1498 // Notify all registered StatusCallbacks for this subGroup 1499 for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { 1500 if (isCallbackPermissioned(cbInfo, mSubGroup)) { 1501 Binder.withCleanCallingIdentity(() -> { 1502 try { 1503 cbInfo.mCallback.onGatewayConnectionError( 1504 gatewayConnectionName, 1505 errorCode, 1506 exceptionClass, 1507 exceptionMessage); 1508 } catch (RemoteException e) { 1509 logDbg("VcnStatusCallback threw on VCN status change", e); 1510 } 1511 }); 1512 } 1513 } 1514 } 1515 } 1516 } 1517 } 1518