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