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

Commit 46e3ac8b authored by Jeremy Joslin's avatar Jeremy Joslin
Browse files

Implemented requestNetwork with a PendingIntent.

ConnectivityManager.requestNetwork(NetworkRequest, PendingIntent)
was unhidden and implemented.

Added ConnectivityManager.removePendingIntentRequest(PendingIntent) as
the companion method.

Bug: 17356414
Change-Id: I656a1e149cc1292c443ebfe9e61ee3eb5a80f143
parent 418f4ed0
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -16896,9 +16896,11 @@ package android.net {
    method public boolean isDefaultNetworkActive();
    method public static boolean isNetworkTypeValid(int);
    method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
    method public void releaseNetworkRequest(android.app.PendingIntent);
    method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
    method public void reportBadNetwork(android.net.Network);
    method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
    method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
    method public deprecated boolean requestRouteToHost(int, int);
    method public deprecated void setNetworkPreference(int);
    method public static boolean setProcessDefaultNetwork(android.net.Network);
@@ -16911,6 +16913,8 @@ package android.net {
    field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
    field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
    field public static final deprecated java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
    field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork";
    field public static final java.lang.String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST = "networkRequestNetworkRequest";
    field public static final java.lang.String EXTRA_NETWORK_TYPE = "networkType";
    field public static final java.lang.String EXTRA_NO_CONNECTIVITY = "noConnectivity";
    field public static final java.lang.String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
+32 −9
Original line number Diff line number Diff line
@@ -2378,17 +2378,15 @@ public class ConnectivityManager {

    /**
     * The lookup key for a {@link Network} object included with the intent after
     * succesfully finding a network for the applications request.  Retrieve it with
     * successfully finding a network for the applications request.  Retrieve it with
     * {@link android.content.Intent#getParcelableExtra(String)}.
     * @hide
     */
    public static final String EXTRA_NETWORK_REQUEST_NETWORK = "networkRequestNetwork";

    /**
     * The lookup key for a {@link NetworkRequest} object included with the intent after
     * succesfully finding a network for the applications request.  Retrieve it with
     * successfully finding a network for the applications request.  Retrieve it with
     * {@link android.content.Intent#getParcelableExtra(String)}.
     * @hide
     */
    public static final String EXTRA_NETWORK_REQUEST_NETWORK_REQUEST =
            "networkRequestNetworkRequest";
@@ -2397,7 +2395,7 @@ public class ConnectivityManager {
    /**
     * Request a network to satisfy a set of {@link NetworkCapabilities}.
     *
     * This function behavies identically to the version that takes a NetworkCallback, but instead
     * This function behaves identically to the version that takes a NetworkCallback, but instead
     * of {@link NetworkCallback} a {@link PendingIntent} is used.  This means
     * the request may outlive the calling application and get called back when a suitable
     * network is found.
@@ -2418,20 +2416,45 @@ public class ConnectivityManager {
     * two Intents defined by {@link Intent#filterEquals}), then it will be removed and
     * replaced by this one, effectively releasing the previous {@link NetworkRequest}.
     * <p>
     * The request may be released normally by calling {@link #unregisterNetworkCallback}.
     * The request may be released normally by calling
     * {@link #releaseNetworkRequest(android.app.PendingIntent)}.
     *
     * @param request {@link NetworkRequest} describing this request.
     * @param operation Action to perform when the network is available (corresponds
     *                  to the {@link NetworkCallback#onAvailable} call.  Typically
     *                  comes from {@link PendingIntent#getBroadcast}.
     * @hide
     *                  comes from {@link PendingIntent#getBroadcast}. Cannot be null.
     */
    public void requestNetwork(NetworkRequest request, PendingIntent operation) {
        checkPendingIntent(operation);
        try {
            mService.pendingRequestForNetwork(request.networkCapabilities, operation);
        } catch (RemoteException e) {}
    }

    /**
     * Removes a request made via {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)}
     * <p>
     * This method has the same behavior as {@link #unregisterNetworkCallback} with respect to
     * releasing network resources and disconnecting.
     *
     * @param operation A PendingIntent equal (as defined by {@link Intent#filterEquals}) to the
     *                  PendingIntent passed to
     *                  {@link #requestNetwork(NetworkRequest, android.app.PendingIntent)} with the
     *                  corresponding NetworkRequest you'd like to remove. Cannot be null.
     */
    public void releaseNetworkRequest(PendingIntent operation) {
        checkPendingIntent(operation);
        try {
            mService.releasePendingNetworkRequest(operation);
        } catch (RemoteException e) {}
    }

    private void checkPendingIntent(PendingIntent intent) {
        if (intent == null) {
            throw new IllegalArgumentException("PendingIntent cannot be null.");
        }
    }

    /**
     * Registers to receive notifications about all networks which satisfy the given
     * {@link NetworkRequest}.  The callbacks will continue to be called until
@@ -2448,7 +2471,7 @@ public class ConnectivityManager {
    /**
     * Unregisters callbacks about and possibly releases networks originating from
     * {@link #requestNetwork} and {@link #registerNetworkCallback} calls.  If the
     * given {@code NetworkCallback} had previosuly been used with {@code #requestNetwork},
     * given {@code NetworkCallback} had previously been used with {@code #requestNetwork},
     * any networks that had been connected to only to satisfy that request will be
     * disconnected.
     *
+2 −0
Original line number Diff line number Diff line
@@ -156,6 +156,8 @@ interface IConnectivityManager
    NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
            in PendingIntent operation);

    void releasePendingNetworkRequest(in PendingIntent operation);

    NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
            in Messenger messenger, in IBinder binder);

+168 −28
Original line number Diff line number Diff line
@@ -184,7 +184,8 @@ import javax.net.ssl.SSLSession;
/**
 * @hide
 */
public class ConnectivityService extends IConnectivityManager.Stub {
public class ConnectivityService extends IConnectivityManager.Stub
        implements PendingIntent.OnFinished {
    private static final String TAG = "ConnectivityService";

    private static final boolean DBG = true;
@@ -382,6 +383,19 @@ public class ConnectivityService extends IConnectivityManager.Stub {
     */
    private static final int EVENT_SYSTEM_READY = 25;

    /**
     * used to add a network request with a pending intent
     * includes a NetworkRequestInfo
     */
    private static final int EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT = 26;

    /**
     * used to remove a pending intent and its associated network request.
     * arg1 = UID of caller
     * obj  = PendingIntent
     */
    private static final int EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT = 27;


    /** Handler used for internal events. */
    final private InternalHandler mHandler;
@@ -395,6 +409,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
    private String mNetTransitionWakeLockCausedBy = "";
    private int mNetTransitionWakeLockSerialNumber;
    private int mNetTransitionWakeLockTimeout;
    private final PowerManager.WakeLock mPendingIntentWakeLock;

    private InetAddress mDefaultDns;

@@ -649,6 +664,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
        mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
        mNetTransitionWakeLockTimeout = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_networkTransitionTimeout);
        mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);

        mNetTrackers = new NetworkStateTracker[
                ConnectivityManager.MAX_NETWORK_TYPE+1];
@@ -2131,11 +2147,40 @@ public class ConnectivityService extends IConnectivityManager.Stub {
        }
    }

    // If this method proves to be too slow then we can maintain a separate
    // pendingIntent => NetworkRequestInfo map.
    // This method assumes that every non-null PendingIntent maps to exactly 1 NetworkRequestInfo.
    private NetworkRequestInfo findExistingNetworkRequestInfo(PendingIntent pendingIntent) {
        Intent intent = pendingIntent.getIntent();
        for (Map.Entry<NetworkRequest, NetworkRequestInfo> entry : mNetworkRequests.entrySet()) {
            PendingIntent existingPendingIntent = entry.getValue().mPendingIntent;
            if (existingPendingIntent != null &&
                    existingPendingIntent.getIntent().filterEquals(intent)) {
                return entry.getValue();
            }
        }
        return null;
    }

    private void handleRegisterNetworkRequestWithIntent(Message msg) {
        final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);

        NetworkRequestInfo existingRequest = findExistingNetworkRequestInfo(nri.mPendingIntent);
        if (existingRequest != null) { // remove the existing request.
            if (DBG) log("Replacing " + existingRequest.request + " with "
                    + nri.request + " because their intents matched.");
            handleReleaseNetworkRequest(existingRequest.request, getCallingUid());
        }
        handleRegisterNetworkRequest(msg);
    }

    private void handleRegisterNetworkRequest(Message msg) {
        final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
        final NetworkCapabilities newCap = nri.request.networkCapabilities;
        int score = 0;

        mNetworkRequests.put(nri.request, nri);

        // Check for the best currently alive network that satisfies this request
        NetworkAgentInfo bestNetwork = null;
        for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
@@ -2173,7 +2218,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                mLegacyTypeTracker.add(nri.request.legacyType, bestNetwork);
            }
        }
        mNetworkRequests.put(nri.request, nri);

        if (nri.isRequest) {
            if (DBG) log("sending new NetworkRequest to factories");
            for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {
@@ -2183,6 +2228,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
        }
    }

    private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent,
            int callingUid) {
        NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
        if (nri != null) {
            handleReleaseNetworkRequest(nri.request, callingUid);
        }
    }

    private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) {
        NetworkRequestInfo nri = mNetworkRequests.get(request);
        if (nri != null) {
@@ -2218,11 +2271,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                    }
                }

                // Maintain the illusion.  When this request arrived, we might have preteneded
                // Maintain the illusion.  When this request arrived, we might have pretended
                // that a network connected to serve it, even though the network was already
                // connected.  Now that this request has gone away, we might have to pretend
                // that the network disconnected.  LegacyTypeTracker will generate that
                // phatom disconnect for this type.
                // phantom disconnect for this type.
                NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
                if (nai != null) {
                    mNetworkForRequestId.remove(nri.request.requestId);
@@ -2253,7 +2306,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {

        @Override
        public void handleMessage(Message msg) {
            NetworkInfo info;
            switch (msg.what) {
                case EVENT_EXPIRE_NET_TRANSITION_WAKELOCK:
                case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: {
@@ -2334,6 +2386,14 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                    handleRegisterNetworkRequest(msg);
                    break;
                }
                case EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT: {
                    handleRegisterNetworkRequestWithIntent(msg);
                    break;
                }
                case EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT: {
                    handleReleaseNetworkRequestWithIntent((PendingIntent) msg.obj, msg.arg1);
                    break;
                }
                case EVENT_RELEASE_NETWORK_REQUEST: {
                    handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1);
                    break;
@@ -3347,12 +3407,23 @@ public class ConnectivityService extends IConnectivityManager.Stub {
        static final boolean LISTEN = false;

        final NetworkRequest request;
        IBinder mBinder;
        final PendingIntent mPendingIntent;
        private final IBinder mBinder;
        final int mPid;
        final int mUid;
        final Messenger messenger;
        final boolean isRequest;

        NetworkRequestInfo(NetworkRequest r, PendingIntent pi, boolean isRequest) {
            request = r;
            mPendingIntent = pi;
            messenger = null;
            mBinder = null;
            mPid = getCallingPid();
            mUid = getCallingUid();
            this.isRequest = isRequest;
        }

        NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, boolean isRequest) {
            super();
            messenger = m;
@@ -3361,6 +3432,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
            mPid = getCallingPid();
            mUid = getCallingUid();
            this.isRequest = isRequest;
            mPendingIntent = null;

            try {
                mBinder.linkToDeath(this, 0);
@@ -3370,8 +3442,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
        }

        void unlinkDeathRecipient() {
            if (mBinder != null) {
                mBinder.unlinkToDeath(this, 0);
            }
        }

        public void binderDied() {
            log("ConnectivityService NetworkRequestInfo binderDied(" +
@@ -3381,22 +3455,46 @@ public class ConnectivityService extends IConnectivityManager.Stub {

        public String toString() {
            return (isRequest ? "Request" : "Listen") + " from uid/pid:" + mUid + "/" +
                    mPid + " for " + request;
                    mPid + " for " + request +
                    (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
        }
    }

    @Override
    public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
            Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
        networkCapabilities = new NetworkCapabilities(networkCapabilities);
        enforceNetworkRequestPermissions(networkCapabilities);
        enforceMeteredApnPolicy(networkCapabilities);

        if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
            throw new IllegalArgumentException("Bad timeout specified");
        }

        NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                nextNetworkRequestId());
        if (DBG) log("requestNetwork for " + networkRequest);
        NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
                NetworkRequestInfo.REQUEST);

        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
        if (timeoutMs > 0) {
            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST,
                    nri), timeoutMs);
        }
        return networkRequest;
    }

    private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities) {
        if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
                == false) {
            enforceConnectivityInternalPermission();
        } else {
            enforceChangePermission();
        }
    }

        networkCapabilities = new NetworkCapabilities(networkCapabilities);

    private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) {
        // if UID is restricted, don't allow them to bring up metered APNs
        if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
                == false) {
@@ -3411,29 +3509,30 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
            }
        }

        if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
            throw new IllegalArgumentException("Bad timeout specified");
    }
        NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,

    @Override
    public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
            PendingIntent operation) {
        checkNotNull(operation, "PendingIntent cannot be null.");
        networkCapabilities = new NetworkCapabilities(networkCapabilities);
        enforceNetworkRequestPermissions(networkCapabilities);
        enforceMeteredApnPolicy(networkCapabilities);

        NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
                nextNetworkRequestId());
        if (DBG) log("requestNetwork for " + networkRequest);
        NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder,
        if (DBG) log("pendingRequest for " + networkRequest + " to trigger " + operation);
        NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation,
                NetworkRequestInfo.REQUEST);

        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST, nri));
        if (timeoutMs > 0) {
            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NETWORK_REQUEST,
                    nri), timeoutMs);
        }
        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_REQUEST_WITH_INTENT,
                nri));
        return networkRequest;
    }

    @Override
    public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
            PendingIntent operation) {
        // TODO
        return null;
    public void releasePendingNetworkRequest(PendingIntent operation) {
        mHandler.sendMessage(mHandler.obtainMessage(EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT,
                getCallingUid(), 0, operation));
    }

    @Override
@@ -3727,6 +3826,39 @@ public class ConnectivityService extends IConnectivityManager.Stub {
        }
    }

    private void sendPendingIntentForRequest(NetworkRequestInfo nri, NetworkAgentInfo networkAgent,
            int notificationType) {
        if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE) {
            Intent intent = new Intent();
            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST_NETWORK, nri.request);
            intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST_NETWORK_REQUEST,
                    networkAgent.network);
            sendIntent(nri.mPendingIntent, intent);
        }
        // else not handled
    }

    private void sendIntent(PendingIntent pendingIntent, Intent intent) {
        mPendingIntentWakeLock.acquire();
        try {
            if (DBG) log("Sending " + pendingIntent);
            pendingIntent.send(mContext, 0, intent, this /* onFinished */, null /* Handler */);
        } catch (PendingIntent.CanceledException e) {
            if (DBG) log(pendingIntent + " was not sent, it had been canceled.");
            mPendingIntentWakeLock.release();
            releasePendingNetworkRequest(pendingIntent);
        }
        // ...otherwise, mPendingIntentWakeLock.release() gets called by onSendFinished()
    }

    @Override
    public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
            String resultData, Bundle resultExtras) {
        if (DBG) log("Finished sending " + pendingIntent);
        mPendingIntentWakeLock.release();
        releasePendingNetworkRequest(pendingIntent);
    }

    private void callCallbackForRequest(NetworkRequestInfo nri,
            NetworkAgentInfo networkAgent, int notificationType) {
        if (nri.messenger == null) return;  // Default request has no msgr
@@ -4137,7 +4269,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
//        } else if (nai.networkMonitor.isEvaluating()) {
//            notifyType = NetworkCallbacks.callCallbackForRequest(request, nai, notifyType);
//        }
        if (nri.mPendingIntent == null) {
            callCallbackForRequest(nri, nai, notifyType);
        } else {
            sendPendingIntentForRequest(nri, nai, notifyType);
        }
    }

    private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, boolean connected, int type) {
@@ -4196,7 +4332,11 @@ public class ConnectivityService extends IConnectivityManager.Stub {
            NetworkRequest nr = networkAgent.networkRequests.valueAt(i);
            NetworkRequestInfo nri = mNetworkRequests.get(nr);
            if (VDBG) log(" sending notification for " + nr);
            if (nri.mPendingIntent == null) {
                callCallbackForRequest(nri, networkAgent, notifyType);
            } else {
                sendPendingIntentForRequest(nri, networkAgent, notifyType);
            }
        }
    }