Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit efdfa53d authored by Automerger Merge Worker's avatar Automerger Merge Worker Committed by Android (Google) Code Review
Browse files

Merge "Merge changes I3242ad0e,I5daced20,Iefd284ae am: f1d78f17 am:...

Merge "Merge changes I3242ad0e,I5daced20,Iefd284ae am: f1d78f17 am: 3371693f am: 0fd3dfe0" into sc-dev
parents 2b2fab39 3de749bc
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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);
}
+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
+119 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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.
@@ -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()));
        }
    }
}
+146 −13
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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();

@@ -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<>();
@@ -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");
@@ -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}. */
@@ -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. */
@@ -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() {
@@ -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
@@ -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;
@@ -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)) {
@@ -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());
                }
            }
        }
    }
+19 −19
Original line number Diff line number Diff line
@@ -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;
@@ -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 =
@@ -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());
    }

@@ -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();

@@ -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);
@@ -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(
@@ -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