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