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