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