Loading core/java/android/net/vcn/IVcnManagementService.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.net.vcn; import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnUnderlyingNetworkPolicy; Loading @@ -33,4 +34,7 @@ interface IVcnManagementService { void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(in NetworkCapabilities nc, in LinkProperties lp); void registerVcnStatusCallback(in ParcelUuid subscriptionGroup, in IVcnStatusCallback callback, in String opPkgName); void unregisterVcnStatusCallback(in IVcnStatusCallback callback); } core/java/android/net/vcn/IVcnStatusCallback.aidl 0 → 100644 +22 −0 Original line number Diff line number Diff line /* * Copyright 2021, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.vcn; /** @hide */ interface IVcnStatusCallback { void onEnteredSafeMode(); } No newline at end of file core/java/android/net/vcn/VcnManager.java +119 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.annotation.SystemService; import android.content.Context; import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.os.Binder; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceSpecificException; Loading Loading @@ -261,6 +262,100 @@ public class VcnManager { } } // TODO: make VcnStatusCallback @SystemApi /** * VcnStatusCallback is the interface for Carrier apps to receive updates for their VCNs. * * <p>VcnStatusCallbacks may be registered before {@link VcnConfig}s are provided for a * subscription group. * * @hide */ public abstract static class VcnStatusCallback { private VcnStatusCallbackBinder mCbBinder; /** * Invoked when the VCN for this Callback's subscription group enters safe mode. * * <p>A VCN will be put into safe mode if any of the gateway connections were unable to * establish a connection within a system-determined timeout (while underlying networks were * available). * * <p>A VCN-configuring app may opt to exit safe mode by (re)setting the VCN configuration * via {@link #setVcnConfig(ParcelUuid, VcnConfig)}. */ public abstract void onEnteredSafeMode(); } /** * Registers the given callback to receive status updates for the specified subscription. * * <p>Callbacks can be registered for a subscription before {@link VcnConfig}s are set for it. * * <p>A {@link VcnStatusCallback} may only be registered for one subscription at a time. {@link * VcnStatusCallback}s may be reused once unregistered. * * <p>A {@link VcnStatusCallback} will only be invoked if the registering package has carrier * privileges for the specified subscription at the time of invocation. * * @param subscriptionGroup The subscription group to match for callbacks * @param executor The {@link Executor} to be used for invoking callbacks * @param callback The VcnStatusCallback to be registered * @throws IllegalStateException if callback is currently registered with VcnManager * @hide */ public void registerVcnStatusCallback( @NonNull ParcelUuid subscriptionGroup, @NonNull Executor executor, @NonNull VcnStatusCallback callback) { requireNonNull(subscriptionGroup, "subscriptionGroup must not be null"); requireNonNull(executor, "executor must not be null"); requireNonNull(callback, "callback must not be null"); synchronized (callback) { if (callback.mCbBinder != null) { throw new IllegalStateException("callback is already registered with VcnManager"); } callback.mCbBinder = new VcnStatusCallbackBinder(executor, callback); try { mService.registerVcnStatusCallback( subscriptionGroup, callback.mCbBinder, mContext.getOpPackageName()); } catch (RemoteException e) { callback.mCbBinder = null; throw e.rethrowFromSystemServer(); } } } /** * Unregisters the given callback. * * <p>Once unregistered, the callback will stop receiving status updates for the subscription it * was registered with. * * @param callback The callback to be unregistered * @hide */ public void unregisterVcnStatusCallback(@NonNull VcnStatusCallback callback) { requireNonNull(callback, "callback must not be null"); synchronized (callback) { if (callback.mCbBinder == null) { // no Binder attached to this callback, so it's not currently registered return; } try { mService.unregisterVcnStatusCallback(callback.mCbBinder); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { callback.mCbBinder = null; } } } /** * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System * Server. Loading @@ -280,7 +375,30 @@ public class VcnManager { @Override public void onPolicyChanged() { mExecutor.execute(() -> mListener.onPolicyChanged()); Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> mListener.onPolicyChanged())); } } /** * Binder wrapper for VcnStatusCallbacks to receive signals from VcnManagementService. * * @hide */ private class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub { @NonNull private final Executor mExecutor; @NonNull private final VcnStatusCallback mCallback; private VcnStatusCallbackBinder( @NonNull Executor executor, @NonNull VcnStatusCallback callback) { mExecutor = executor; mCallback = callback; } @Override public void onEnteredSafeMode() { Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> mCallback.onEnteredSafeMode())); } } } services/core/java/com/android/server/VcnManagementService.java +146 −13 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubsc import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.net.ConnectivityManager; Loading @@ -29,6 +30,7 @@ import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.net.TelephonyNetworkSpecifier; import android.net.vcn.IVcnManagementService; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnUnderlyingNetworkPolicy; Loading @@ -54,6 +56,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.LocationPermissionChecker; import com.android.server.vcn.TelephonySubscriptionTracker; import com.android.server.vcn.Vcn; import com.android.server.vcn.VcnContext; Loading Loading @@ -124,6 +127,7 @@ import java.util.concurrent.TimeUnit; * * @hide */ // TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private static final String TAG = VcnManagementService.class.getSimpleName(); Loading @@ -147,6 +151,9 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker; @NonNull private final VcnContext mVcnContext; /** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */ @Nullable private LocationPermissionChecker mLocationPermissionChecker; @GuardedBy("mLock") @NonNull private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>(); Loading @@ -169,6 +176,10 @@ public class VcnManagementService extends IVcnManagementService.Stub { private final Map<IBinder, PolicyListenerBinderDeath> mRegisteredPolicyListeners = new ArrayMap<>(); @GuardedBy("mLock") @NonNull private final Map<IBinder, VcnStatusCallbackInfo> mRegisteredStatusCallbacks = new ArrayMap<>(); @VisibleForTesting(visibility = Visibility.PRIVATE) VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) { mContext = requireNonNull(context, "Missing context"); Loading Loading @@ -293,8 +304,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnSafemodeCallback safemodeCallback) { return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback); @NonNull VcnSafeModeCallback safeModeCallback) { return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safeModeCallback); } /** Gets the subId indicated by the given {@link WifiInfo}. */ Loading @@ -302,6 +313,11 @@ public class VcnManagementService extends IVcnManagementService.Stub { // TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId return SubscriptionManager.INVALID_SUBSCRIPTION_ID; } /** Creates a new LocationPermissionChecker for the provided Context. */ public LocationPermissionChecker newLocationPermissionChecker(@NonNull Context context) { return new LocationPermissionChecker(context); } } /** Notifies the VcnManagementService that external dependencies can be set up. */ Loading @@ -309,6 +325,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { mContext.getSystemService(ConnectivityManager.class) .registerNetworkProvider(mNetworkProvider); mTelephonySubscriptionTracker.register(); mLocationPermissionChecker = mDeps.newLocationPermissionChecker(mVcnContext.getContext()); } private void enforcePrimaryUser() { Loading Loading @@ -440,12 +457,12 @@ public class VcnManagementService extends IVcnManagementService.Stub { // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active // VCN. final VcnSafemodeCallbackImpl safemodeCallback = new VcnSafemodeCallbackImpl(subscriptionGroup); final VcnSafeModeCallbackImpl safeModeCallback = new VcnSafeModeCallbackImpl(subscriptionGroup); final Vcn newInstance = mDeps.newVcn( mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback); mVcnContext, subscriptionGroup, config, mLastSnapshot, safeModeCallback); mVcns.put(subscriptionGroup, newInstance); // Now that a new VCN has started, notify all registered listeners to refresh their Loading Loading @@ -551,6 +568,14 @@ public class VcnManagementService extends IVcnManagementService.Stub { } } /** Get current VcnStatusCallbacks for testing purposes. */ @VisibleForTesting(visibility = Visibility.PRIVATE) public Map<IBinder, VcnStatusCallbackInfo> getAllStatusCallbacks() { synchronized (mLock) { return Collections.unmodifiableMap(mRegisteredStatusCallbacks); } } /** Binder death recipient used to remove a registered policy listener. */ private class PolicyListenerBinderDeath implements Binder.DeathRecipient { @NonNull private final IVcnUnderlyingNetworkPolicyListener mListener; Loading Loading @@ -672,22 +697,109 @@ public class VcnManagementService extends IVcnManagementService.Stub { return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities); } /** Callback for signalling when a Vcn has entered Safemode. */ public interface VcnSafemodeCallback { /** Called by a Vcn to signal that it has entered Safemode. */ void onEnteredSafemode(); /** Binder death recipient used to remove registered VcnStatusCallbacks. */ @VisibleForTesting(visibility = Visibility.PRIVATE) class VcnStatusCallbackInfo implements Binder.DeathRecipient { @NonNull final ParcelUuid mSubGroup; @NonNull final IVcnStatusCallback mCallback; @NonNull final String mPkgName; final int mUid; private VcnStatusCallbackInfo( @NonNull ParcelUuid subGroup, @NonNull IVcnStatusCallback callback, @NonNull String pkgName, int uid) { mSubGroup = subGroup; mCallback = callback; mPkgName = pkgName; mUid = uid; } @Override public void binderDied() { Log.e(TAG, "app died without unregistering VcnStatusCallback"); unregisterVcnStatusCallback(mCallback); } } /** Registers the provided callback for receiving VCN status updates. */ @Override public void registerVcnStatusCallback( @NonNull ParcelUuid subGroup, @NonNull IVcnStatusCallback callback, @NonNull String opPkgName) { final int callingUid = mDeps.getBinderCallingUid(); final long identity = Binder.clearCallingIdentity(); try { requireNonNull(subGroup, "subGroup must not be null"); requireNonNull(callback, "callback must not be null"); requireNonNull(opPkgName, "opPkgName must not be null"); mContext.getSystemService(AppOpsManager.class).checkPackage(callingUid, opPkgName); final IBinder cbBinder = callback.asBinder(); final VcnStatusCallbackInfo cbInfo = new VcnStatusCallbackInfo( subGroup, callback, opPkgName, mDeps.getBinderCallingUid()); try { cbBinder.linkToDeath(cbInfo, 0 /* flags */); } catch (RemoteException e) { // Remote binder already died - don't add to mRegisteredStatusCallbacks and exit return; } /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */ private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback { synchronized (mLock) { if (mRegisteredStatusCallbacks.containsKey(cbBinder)) { throw new IllegalStateException( "Attempting to register a callback that is already in use"); } mRegisteredStatusCallbacks.put(cbBinder, cbInfo); } } finally { Binder.restoreCallingIdentity(identity); } } /** Unregisters the provided callback from receiving future VCN status updates. */ @Override public void unregisterVcnStatusCallback(@NonNull IVcnStatusCallback callback) { final long identity = Binder.clearCallingIdentity(); try { requireNonNull(callback, "callback must not be null"); final IBinder cbBinder = callback.asBinder(); synchronized (mLock) { VcnStatusCallbackInfo cbInfo = mRegisteredStatusCallbacks.remove(cbBinder); if (cbInfo != null) { cbBinder.unlinkToDeath(cbInfo, 0 /* flags */); } } } finally { Binder.restoreCallingIdentity(identity); } } // TODO(b/180452282): Make name more generic and implement directly with VcnManagementService /** Callback for signalling when a Vcn has entered safe mode. */ public interface VcnSafeModeCallback { /** Called by a Vcn to signal that it has entered safe mode. */ void onEnteredSafeMode(); } /** VcnSafeModeCallback is used by Vcns to notify VcnManagementService on entering safe mode. */ private class VcnSafeModeCallbackImpl implements VcnSafeModeCallback { @NonNull private final ParcelUuid mSubGroup; private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) { private VcnSafeModeCallbackImpl(@NonNull final ParcelUuid subGroup) { mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup"); } @Override public void onEnteredSafemode() { public void onEnteredSafeMode() { synchronized (mLock) { // Ignore if this subscription group doesn't exist anymore if (!mVcns.containsKey(mSubGroup)) { Loading @@ -695,6 +807,27 @@ public class VcnManagementService extends IVcnManagementService.Stub { } notifyAllPolicyListenersLocked(); // Notify all registered StatusCallbacks for this subGroup for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { if (!mSubGroup.equals(cbInfo.mSubGroup)) { continue; } if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup( mSubGroup, cbInfo.mPkgName)) { continue; } if (!mLocationPermissionChecker.checkLocationPermission( cbInfo.mPkgName, "VcnStatusCallback" /* featureId */, cbInfo.mUid, null /* message */)) { continue; } Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode()); } } } } Loading services/core/java/com/android/server/vcn/Vcn.java +19 −19 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.server.VcnManagementService.VcnSafemodeCallback; import com.android.server.VcnManagementService.VcnSafeModeCallback; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import java.util.Collections; Loading Loading @@ -86,18 +86,18 @@ public class Vcn extends Handler { private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE; /** * Causes this VCN to immediately enter Safemode. * Causes this VCN to immediately enter safe mode. * * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode. * <p>Upon entering safe mode, the VCN will unregister its RequestListener, tear down all of its * VcnGatewayConnections, and notify VcnManagementService that it is in safe mode. */ private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1; private static final int MSG_CMD_ENTER_SAFE_MODE = MSG_CMD_BASE + 1; @NonNull private final VcnContext mVcnContext; @NonNull private final ParcelUuid mSubscriptionGroup; @NonNull private final Dependencies mDeps; @NonNull private final VcnNetworkRequestListener mRequestListener; @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback; @NonNull private final VcnSafeModeCallback mVcnSafeModeCallback; @NonNull private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections = Loading Loading @@ -125,13 +125,13 @@ public class Vcn extends Handler { @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnSafemodeCallback vcnSafemodeCallback) { @NonNull VcnSafeModeCallback vcnSafeModeCallback) { this( vcnContext, subscriptionGroup, config, snapshot, vcnSafemodeCallback, vcnSafeModeCallback, new Dependencies()); } Loading @@ -141,13 +141,13 @@ public class Vcn extends Handler { @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnSafemodeCallback vcnSafemodeCallback, @NonNull VcnSafeModeCallback vcnSafeModeCallback, @NonNull Dependencies deps) { super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); mVcnContext = vcnContext; mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); mVcnSafemodeCallback = Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback"); mVcnSafeModeCallback = Objects.requireNonNull(vcnSafeModeCallback, "Missing vcnSafeModeCallback"); mDeps = Objects.requireNonNull(deps, "Missing deps"); mRequestListener = new VcnNetworkRequestListener(); Loading Loading @@ -216,8 +216,8 @@ public class Vcn extends Handler { case MSG_CMD_TEARDOWN: handleTeardown(); break; case MSG_CMD_ENTER_SAFEMODE: handleEnterSafemode(); case MSG_CMD_ENTER_SAFE_MODE: handleEnterSafeMode(); break; default: Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what); Loading @@ -243,10 +243,10 @@ public class Vcn extends Handler { mIsActive.set(false); } private void handleEnterSafemode() { private void handleEnterSafeMode() { handleTeardown(); mVcnSafemodeCallback.onEnteredSafemode(); mVcnSafeModeCallback.onEnteredSafeMode(); } private void handleNetworkRequested( Loading Loading @@ -335,14 +335,14 @@ public class Vcn extends Handler { /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */ @VisibleForTesting(visibility = Visibility.PACKAGE) public interface VcnGatewayStatusCallback { /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */ void onEnteredSafemode(); /** Called by a VcnGatewayConnection to indicate that it has entered safe mode. */ void onEnteredSafeMode(); } private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback { @Override public void onEnteredSafemode() { sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE)); public void onEnteredSafeMode() { sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE)); } } Loading Loading
core/java/android/net/vcn/IVcnManagementService.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.net.vcn; import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnUnderlyingNetworkPolicy; Loading @@ -33,4 +34,7 @@ interface IVcnManagementService { void addVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); void removeVcnUnderlyingNetworkPolicyListener(in IVcnUnderlyingNetworkPolicyListener listener); VcnUnderlyingNetworkPolicy getUnderlyingNetworkPolicy(in NetworkCapabilities nc, in LinkProperties lp); void registerVcnStatusCallback(in ParcelUuid subscriptionGroup, in IVcnStatusCallback callback, in String opPkgName); void unregisterVcnStatusCallback(in IVcnStatusCallback callback); }
core/java/android/net/vcn/IVcnStatusCallback.aidl 0 → 100644 +22 −0 Original line number Diff line number Diff line /* * Copyright 2021, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net.vcn; /** @hide */ interface IVcnStatusCallback { void onEnteredSafeMode(); } No newline at end of file
core/java/android/net/vcn/VcnManager.java +119 −1 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import android.annotation.SystemService; import android.content.Context; import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.os.Binder; import android.os.ParcelUuid; import android.os.RemoteException; import android.os.ServiceSpecificException; Loading Loading @@ -261,6 +262,100 @@ public class VcnManager { } } // TODO: make VcnStatusCallback @SystemApi /** * VcnStatusCallback is the interface for Carrier apps to receive updates for their VCNs. * * <p>VcnStatusCallbacks may be registered before {@link VcnConfig}s are provided for a * subscription group. * * @hide */ public abstract static class VcnStatusCallback { private VcnStatusCallbackBinder mCbBinder; /** * Invoked when the VCN for this Callback's subscription group enters safe mode. * * <p>A VCN will be put into safe mode if any of the gateway connections were unable to * establish a connection within a system-determined timeout (while underlying networks were * available). * * <p>A VCN-configuring app may opt to exit safe mode by (re)setting the VCN configuration * via {@link #setVcnConfig(ParcelUuid, VcnConfig)}. */ public abstract void onEnteredSafeMode(); } /** * Registers the given callback to receive status updates for the specified subscription. * * <p>Callbacks can be registered for a subscription before {@link VcnConfig}s are set for it. * * <p>A {@link VcnStatusCallback} may only be registered for one subscription at a time. {@link * VcnStatusCallback}s may be reused once unregistered. * * <p>A {@link VcnStatusCallback} will only be invoked if the registering package has carrier * privileges for the specified subscription at the time of invocation. * * @param subscriptionGroup The subscription group to match for callbacks * @param executor The {@link Executor} to be used for invoking callbacks * @param callback The VcnStatusCallback to be registered * @throws IllegalStateException if callback is currently registered with VcnManager * @hide */ public void registerVcnStatusCallback( @NonNull ParcelUuid subscriptionGroup, @NonNull Executor executor, @NonNull VcnStatusCallback callback) { requireNonNull(subscriptionGroup, "subscriptionGroup must not be null"); requireNonNull(executor, "executor must not be null"); requireNonNull(callback, "callback must not be null"); synchronized (callback) { if (callback.mCbBinder != null) { throw new IllegalStateException("callback is already registered with VcnManager"); } callback.mCbBinder = new VcnStatusCallbackBinder(executor, callback); try { mService.registerVcnStatusCallback( subscriptionGroup, callback.mCbBinder, mContext.getOpPackageName()); } catch (RemoteException e) { callback.mCbBinder = null; throw e.rethrowFromSystemServer(); } } } /** * Unregisters the given callback. * * <p>Once unregistered, the callback will stop receiving status updates for the subscription it * was registered with. * * @param callback The callback to be unregistered * @hide */ public void unregisterVcnStatusCallback(@NonNull VcnStatusCallback callback) { requireNonNull(callback, "callback must not be null"); synchronized (callback) { if (callback.mCbBinder == null) { // no Binder attached to this callback, so it's not currently registered return; } try { mService.unregisterVcnStatusCallback(callback.mCbBinder); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } finally { callback.mCbBinder = null; } } } /** * Binder wrapper for added VcnUnderlyingNetworkPolicyListeners to receive signals from System * Server. Loading @@ -280,7 +375,30 @@ public class VcnManager { @Override public void onPolicyChanged() { mExecutor.execute(() -> mListener.onPolicyChanged()); Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> mListener.onPolicyChanged())); } } /** * Binder wrapper for VcnStatusCallbacks to receive signals from VcnManagementService. * * @hide */ private class VcnStatusCallbackBinder extends IVcnStatusCallback.Stub { @NonNull private final Executor mExecutor; @NonNull private final VcnStatusCallback mCallback; private VcnStatusCallbackBinder( @NonNull Executor executor, @NonNull VcnStatusCallback callback) { mExecutor = executor; mCallback = callback; } @Override public void onEnteredSafeMode() { Binder.withCleanCallingIdentity( () -> mExecutor.execute(() -> mCallback.onEnteredSafeMode())); } } }
services/core/java/com/android/server/VcnManagementService.java +146 −13 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubsc import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.net.ConnectivityManager; Loading @@ -29,6 +30,7 @@ import android.net.LinkProperties; import android.net.NetworkCapabilities; import android.net.TelephonyNetworkSpecifier; import android.net.vcn.IVcnManagementService; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnUnderlyingNetworkPolicy; Loading @@ -54,6 +56,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.LocationPermissionChecker; import com.android.server.vcn.TelephonySubscriptionTracker; import com.android.server.vcn.Vcn; import com.android.server.vcn.VcnContext; Loading Loading @@ -124,6 +127,7 @@ import java.util.concurrent.TimeUnit; * * @hide */ // TODO(b/180451994): ensure all incoming + outgoing calls have a cleared calling identity public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private static final String TAG = VcnManagementService.class.getSimpleName(); Loading @@ -147,6 +151,9 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker; @NonNull private final VcnContext mVcnContext; /** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */ @Nullable private LocationPermissionChecker mLocationPermissionChecker; @GuardedBy("mLock") @NonNull private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>(); Loading @@ -169,6 +176,10 @@ public class VcnManagementService extends IVcnManagementService.Stub { private final Map<IBinder, PolicyListenerBinderDeath> mRegisteredPolicyListeners = new ArrayMap<>(); @GuardedBy("mLock") @NonNull private final Map<IBinder, VcnStatusCallbackInfo> mRegisteredStatusCallbacks = new ArrayMap<>(); @VisibleForTesting(visibility = Visibility.PRIVATE) VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) { mContext = requireNonNull(context, "Missing context"); Loading Loading @@ -293,8 +304,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnSafemodeCallback safemodeCallback) { return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safemodeCallback); @NonNull VcnSafeModeCallback safeModeCallback) { return new Vcn(vcnContext, subscriptionGroup, config, snapshot, safeModeCallback); } /** Gets the subId indicated by the given {@link WifiInfo}. */ Loading @@ -302,6 +313,11 @@ public class VcnManagementService extends IVcnManagementService.Stub { // TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId return SubscriptionManager.INVALID_SUBSCRIPTION_ID; } /** Creates a new LocationPermissionChecker for the provided Context. */ public LocationPermissionChecker newLocationPermissionChecker(@NonNull Context context) { return new LocationPermissionChecker(context); } } /** Notifies the VcnManagementService that external dependencies can be set up. */ Loading @@ -309,6 +325,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { mContext.getSystemService(ConnectivityManager.class) .registerNetworkProvider(mNetworkProvider); mTelephonySubscriptionTracker.register(); mLocationPermissionChecker = mDeps.newLocationPermissionChecker(mVcnContext.getContext()); } private void enforcePrimaryUser() { Loading Loading @@ -440,12 +457,12 @@ public class VcnManagementService extends IVcnManagementService.Stub { // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active // VCN. final VcnSafemodeCallbackImpl safemodeCallback = new VcnSafemodeCallbackImpl(subscriptionGroup); final VcnSafeModeCallbackImpl safeModeCallback = new VcnSafeModeCallbackImpl(subscriptionGroup); final Vcn newInstance = mDeps.newVcn( mVcnContext, subscriptionGroup, config, mLastSnapshot, safemodeCallback); mVcnContext, subscriptionGroup, config, mLastSnapshot, safeModeCallback); mVcns.put(subscriptionGroup, newInstance); // Now that a new VCN has started, notify all registered listeners to refresh their Loading Loading @@ -551,6 +568,14 @@ public class VcnManagementService extends IVcnManagementService.Stub { } } /** Get current VcnStatusCallbacks for testing purposes. */ @VisibleForTesting(visibility = Visibility.PRIVATE) public Map<IBinder, VcnStatusCallbackInfo> getAllStatusCallbacks() { synchronized (mLock) { return Collections.unmodifiableMap(mRegisteredStatusCallbacks); } } /** Binder death recipient used to remove a registered policy listener. */ private class PolicyListenerBinderDeath implements Binder.DeathRecipient { @NonNull private final IVcnUnderlyingNetworkPolicyListener mListener; Loading Loading @@ -672,22 +697,109 @@ public class VcnManagementService extends IVcnManagementService.Stub { return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities); } /** Callback for signalling when a Vcn has entered Safemode. */ public interface VcnSafemodeCallback { /** Called by a Vcn to signal that it has entered Safemode. */ void onEnteredSafemode(); /** Binder death recipient used to remove registered VcnStatusCallbacks. */ @VisibleForTesting(visibility = Visibility.PRIVATE) class VcnStatusCallbackInfo implements Binder.DeathRecipient { @NonNull final ParcelUuid mSubGroup; @NonNull final IVcnStatusCallback mCallback; @NonNull final String mPkgName; final int mUid; private VcnStatusCallbackInfo( @NonNull ParcelUuid subGroup, @NonNull IVcnStatusCallback callback, @NonNull String pkgName, int uid) { mSubGroup = subGroup; mCallback = callback; mPkgName = pkgName; mUid = uid; } @Override public void binderDied() { Log.e(TAG, "app died without unregistering VcnStatusCallback"); unregisterVcnStatusCallback(mCallback); } } /** Registers the provided callback for receiving VCN status updates. */ @Override public void registerVcnStatusCallback( @NonNull ParcelUuid subGroup, @NonNull IVcnStatusCallback callback, @NonNull String opPkgName) { final int callingUid = mDeps.getBinderCallingUid(); final long identity = Binder.clearCallingIdentity(); try { requireNonNull(subGroup, "subGroup must not be null"); requireNonNull(callback, "callback must not be null"); requireNonNull(opPkgName, "opPkgName must not be null"); mContext.getSystemService(AppOpsManager.class).checkPackage(callingUid, opPkgName); final IBinder cbBinder = callback.asBinder(); final VcnStatusCallbackInfo cbInfo = new VcnStatusCallbackInfo( subGroup, callback, opPkgName, mDeps.getBinderCallingUid()); try { cbBinder.linkToDeath(cbInfo, 0 /* flags */); } catch (RemoteException e) { // Remote binder already died - don't add to mRegisteredStatusCallbacks and exit return; } /** VcnSafemodeCallback is used by Vcns to notify VcnManagementService on entering Safemode. */ private class VcnSafemodeCallbackImpl implements VcnSafemodeCallback { synchronized (mLock) { if (mRegisteredStatusCallbacks.containsKey(cbBinder)) { throw new IllegalStateException( "Attempting to register a callback that is already in use"); } mRegisteredStatusCallbacks.put(cbBinder, cbInfo); } } finally { Binder.restoreCallingIdentity(identity); } } /** Unregisters the provided callback from receiving future VCN status updates. */ @Override public void unregisterVcnStatusCallback(@NonNull IVcnStatusCallback callback) { final long identity = Binder.clearCallingIdentity(); try { requireNonNull(callback, "callback must not be null"); final IBinder cbBinder = callback.asBinder(); synchronized (mLock) { VcnStatusCallbackInfo cbInfo = mRegisteredStatusCallbacks.remove(cbBinder); if (cbInfo != null) { cbBinder.unlinkToDeath(cbInfo, 0 /* flags */); } } } finally { Binder.restoreCallingIdentity(identity); } } // TODO(b/180452282): Make name more generic and implement directly with VcnManagementService /** Callback for signalling when a Vcn has entered safe mode. */ public interface VcnSafeModeCallback { /** Called by a Vcn to signal that it has entered safe mode. */ void onEnteredSafeMode(); } /** VcnSafeModeCallback is used by Vcns to notify VcnManagementService on entering safe mode. */ private class VcnSafeModeCallbackImpl implements VcnSafeModeCallback { @NonNull private final ParcelUuid mSubGroup; private VcnSafemodeCallbackImpl(@NonNull final ParcelUuid subGroup) { private VcnSafeModeCallbackImpl(@NonNull final ParcelUuid subGroup) { mSubGroup = Objects.requireNonNull(subGroup, "Missing subGroup"); } @Override public void onEnteredSafemode() { public void onEnteredSafeMode() { synchronized (mLock) { // Ignore if this subscription group doesn't exist anymore if (!mVcns.containsKey(mSubGroup)) { Loading @@ -695,6 +807,27 @@ public class VcnManagementService extends IVcnManagementService.Stub { } notifyAllPolicyListenersLocked(); // Notify all registered StatusCallbacks for this subGroup for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) { if (!mSubGroup.equals(cbInfo.mSubGroup)) { continue; } if (!mLastSnapshot.packageHasPermissionsForSubscriptionGroup( mSubGroup, cbInfo.mPkgName)) { continue; } if (!mLocationPermissionChecker.checkLocationPermission( cbInfo.mPkgName, "VcnStatusCallback" /* featureId */, cbInfo.mUid, null /* message */)) { continue; } Binder.withCleanCallingIdentity(() -> cbInfo.mCallback.onEnteredSafeMode()); } } } } Loading
services/core/java/com/android/server/vcn/Vcn.java +19 −19 Original line number Diff line number Diff line Loading @@ -30,7 +30,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.server.VcnManagementService.VcnSafemodeCallback; import com.android.server.VcnManagementService.VcnSafeModeCallback; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import java.util.Collections; Loading Loading @@ -86,18 +86,18 @@ public class Vcn extends Handler { private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE; /** * Causes this VCN to immediately enter Safemode. * Causes this VCN to immediately enter safe mode. * * <p>Upon entering Safemode, the VCN will unregister its RequestListener, tear down all of its * VcnGatewayConnections, and notify VcnManagementService that it is in Safemode. * <p>Upon entering safe mode, the VCN will unregister its RequestListener, tear down all of its * VcnGatewayConnections, and notify VcnManagementService that it is in safe mode. */ private static final int MSG_CMD_ENTER_SAFEMODE = MSG_CMD_BASE + 1; private static final int MSG_CMD_ENTER_SAFE_MODE = MSG_CMD_BASE + 1; @NonNull private final VcnContext mVcnContext; @NonNull private final ParcelUuid mSubscriptionGroup; @NonNull private final Dependencies mDeps; @NonNull private final VcnNetworkRequestListener mRequestListener; @NonNull private final VcnSafemodeCallback mVcnSafemodeCallback; @NonNull private final VcnSafeModeCallback mVcnSafeModeCallback; @NonNull private final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> mVcnGatewayConnections = Loading Loading @@ -125,13 +125,13 @@ public class Vcn extends Handler { @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnSafemodeCallback vcnSafemodeCallback) { @NonNull VcnSafeModeCallback vcnSafeModeCallback) { this( vcnContext, subscriptionGroup, config, snapshot, vcnSafemodeCallback, vcnSafeModeCallback, new Dependencies()); } Loading @@ -141,13 +141,13 @@ public class Vcn extends Handler { @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config, @NonNull TelephonySubscriptionSnapshot snapshot, @NonNull VcnSafemodeCallback vcnSafemodeCallback, @NonNull VcnSafeModeCallback vcnSafeModeCallback, @NonNull Dependencies deps) { super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); mVcnContext = vcnContext; mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); mVcnSafemodeCallback = Objects.requireNonNull(vcnSafemodeCallback, "Missing vcnSafemodeCallback"); mVcnSafeModeCallback = Objects.requireNonNull(vcnSafeModeCallback, "Missing vcnSafeModeCallback"); mDeps = Objects.requireNonNull(deps, "Missing deps"); mRequestListener = new VcnNetworkRequestListener(); Loading Loading @@ -216,8 +216,8 @@ public class Vcn extends Handler { case MSG_CMD_TEARDOWN: handleTeardown(); break; case MSG_CMD_ENTER_SAFEMODE: handleEnterSafemode(); case MSG_CMD_ENTER_SAFE_MODE: handleEnterSafeMode(); break; default: Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what); Loading @@ -243,10 +243,10 @@ public class Vcn extends Handler { mIsActive.set(false); } private void handleEnterSafemode() { private void handleEnterSafeMode() { handleTeardown(); mVcnSafemodeCallback.onEnteredSafemode(); mVcnSafeModeCallback.onEnteredSafeMode(); } private void handleNetworkRequested( Loading Loading @@ -335,14 +335,14 @@ public class Vcn extends Handler { /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */ @VisibleForTesting(visibility = Visibility.PACKAGE) public interface VcnGatewayStatusCallback { /** Called by a VcnGatewayConnection to indicate that it has entered Safemode. */ void onEnteredSafemode(); /** Called by a VcnGatewayConnection to indicate that it has entered safe mode. */ void onEnteredSafeMode(); } private class VcnGatewayStatusCallbackImpl implements VcnGatewayStatusCallback { @Override public void onEnteredSafemode() { sendMessage(obtainMessage(MSG_CMD_ENTER_SAFEMODE)); public void onEnteredSafeMode() { sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE)); } } Loading