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