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