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