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

Commit 8160544d authored by Jeremy Joslin's avatar Jeremy Joslin
Browse files

Enforce the calling permissions within NetworkRecommendationProvider.

Had to add a new ctor to get a Context instance and deprecate the
single arg ctor. Since this was being done I also took the opportunity
to replace the Handler param with an Executor which is more
appropriate in this case.

I'll remove the deprecated ctor once I've updated all of its
call points.

Test: adb shell am instrument -e class android.net.NetworkRecommendationProviderTest -w com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
Bug: 34518584
Change-Id: Ic2441655d69838ae9caa7d598e876dec36e15363
parent eb30161a
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -27066,7 +27066,8 @@ package android.net {
  }
  public abstract class NetworkRecommendationProvider {
    ctor public NetworkRecommendationProvider(android.os.Handler);
    ctor public deprecated NetworkRecommendationProvider(android.os.Handler);
    ctor public NetworkRecommendationProvider(android.content.Context, java.util.concurrent.Executor);
    method public final android.os.IBinder getBinder();
    method public abstract void onRequestRecommendation(android.net.RecommendationRequest, android.net.NetworkRecommendationProvider.ResultCallback);
    method public abstract void onRequestScores(android.net.NetworkKey[]);
+45 −2
Original line number Diff line number Diff line
package android.net;

import android.Manifest.permission;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -10,8 +12,10 @@ import android.os.RemoteException;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;

import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;

/**
@@ -32,6 +36,7 @@ public abstract class NetworkRecommendationProvider {
    /**
     * Constructs a new instance.
     * @param handler indicates which thread to use when handling requests. Cannot be {@code null}.
     * @deprecated use {@link #NetworkRecommendationProvider(Context, Executor)}
     */
    public NetworkRecommendationProvider(Handler handler) {
        if (handler == null) {
@@ -40,6 +45,17 @@ public abstract class NetworkRecommendationProvider {
        mService = new ServiceWrapper(handler);
    }

    /**
     * Constructs a new instance.
     * @param context the current context instance. Cannot be {@code null}.
     * @param executor used to execute the incoming requests. Cannot be {@code null}.
     */
    public NetworkRecommendationProvider(Context context, Executor executor) {
        Preconditions.checkNotNull(context);
        Preconditions.checkNotNull(executor);
        mService = new ServiceWrapper(context, executor);
    }

    /**
     * Invoked when a recommendation has been requested.
     *
@@ -130,17 +146,28 @@ public abstract class NetworkRecommendationProvider {
     * A wrapper around INetworkRecommendationProvider that dispatches to the provided Handler.
     */
    private final class ServiceWrapper extends INetworkRecommendationProvider.Stub {
        private final Context mContext;
        private final Executor mExecutor;
        private final Handler mHandler;

        ServiceWrapper(Handler handler) {
            mHandler = handler;
            mExecutor = null;
            mContext = null;
        }

        ServiceWrapper(Context context, Executor executor) {
            mContext = context;
            mExecutor = executor;
            mHandler = null;
        }

        @Override
        public void requestRecommendation(final RecommendationRequest request,
                final IRemoteCallback callback, final int sequence) throws RemoteException {
            enforceCallingPermission();
            if (VERBOSE) Log.v(TAG, "requestRecommendation(seq=" + sequence + ")");
            mHandler.post(new Runnable() {
            execute(new Runnable() {
                @Override
                public void run() {
                    if (VERBOSE) {
@@ -154,8 +181,9 @@ public abstract class NetworkRecommendationProvider {

        @Override
        public void requestScores(final NetworkKey[] networks) throws RemoteException {
            enforceCallingPermission();
            if (networks != null && networks.length > 0) {
                mHandler.post(new Runnable() {
                execute(new Runnable() {
                    @Override
                    public void run() {
                        onRequestScores(networks);
@@ -163,5 +191,20 @@ public abstract class NetworkRecommendationProvider {
                });
            }
        }

        private void execute(Runnable command) {
            if (mExecutor != null) {
                mExecutor.execute(command);
            } else {
                mHandler.post(command);
            }
        }

        private void enforceCallingPermission() {
            if (mContext != null) {
                mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES,
                        "Permission denied.");
            }
        }
    }
}
+60 −29
Original line number Diff line number Diff line
@@ -3,61 +3,62 @@ package android.net;
import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;
import static android.net.NetworkRecommendationProvider.EXTRA_SEQUENCE;

import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.fail;
import static junit.framework.TestCase.assertEquals;

import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;

import android.Manifest.permission;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IRemoteCallback;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Unit test for the {@link NetworkRecommendationProvider}.
 */
public class NetworkRecommendationProviderTest extends InstrumentationTestCase {
@RunWith(AndroidJUnit4.class)
public class NetworkRecommendationProviderTest {
    @Mock private IRemoteCallback mMockRemoteCallback;
    @Mock private Context mContext;
    private NetworkRecProvider mRecProvider;
    private Handler mHandler;
    private INetworkRecommendationProvider mStub;
    private CountDownLatch mRecRequestLatch;
    private CountDownLatch mScoreRequestLatch;
    private NetworkKey[] mTestNetworkKeys;

    @Override
    @Before
    public void setUp() throws Exception {
        super.setUp();

        // Configuration needed to make mockito/dexcache work.
        final Context context = getInstrumentation().getTargetContext();
        System.setProperty("dexmaker.dexcache",
                context.getCacheDir().getPath());
        ClassLoader newClassLoader = getInstrumentation().getClass().getClassLoader();
        Thread.currentThread().setContextClassLoader(newClassLoader);

        MockitoAnnotations.initMocks(this);

        HandlerThread thread = new HandlerThread("NetworkRecommendationProviderTest");
        thread.start();
        Executor executor = Executors.newSingleThreadExecutor();
        mRecRequestLatch = new CountDownLatch(1);
        mScoreRequestLatch = new CountDownLatch(1);
        mHandler = new Handler(thread.getLooper());
        mRecProvider = new NetworkRecProvider(mHandler, mRecRequestLatch, mScoreRequestLatch);
        mRecProvider = new NetworkRecProvider(mContext, executor, mRecRequestLatch,
                mScoreRequestLatch);
        mStub = INetworkRecommendationProvider.Stub.asInterface(mRecProvider.getBinder());
        mTestNetworkKeys = new NetworkKey[2];
        mTestNetworkKeys[0] = new NetworkKey(new WifiKey("\"ssid_01\"", "00:00:00:00:00:11"));
        mTestNetworkKeys[1] = new NetworkKey(new WifiKey("\"ssid_02\"", "00:00:00:00:00:22"));
    }

    @MediumTest
    @Test
    public void testRecommendationRequestReceived() throws Exception {
        final RecommendationRequest request = new RecommendationRequest.Builder().build();
        final int sequence = 100;
@@ -71,7 +72,23 @@ public class NetworkRecommendationProviderTest extends InstrumentationTestCase {
        assertEquals(expectedResultCallback, mRecProvider.mCapturedCallback);
    }

    @SmallTest
    @Test
    public void testRecommendationRequest_permissionsEnforced() throws Exception {
        final RecommendationRequest request = new RecommendationRequest.Builder().build();
        final int sequence = 100;
        Mockito.doThrow(new SecurityException())
                .when(mContext)
                .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES), anyString());

        try {
            mStub.requestRecommendation(request, mMockRemoteCallback, sequence);
            fail("SecurityException expected.");
        } catch (SecurityException e) {
            // expected
        }
    }

    @Test
    public void testResultCallbackOnResult() throws Exception {
        final int sequence = 100;
        final NetworkRecommendationProvider.ResultCallback callback =
@@ -87,7 +104,7 @@ public class NetworkRecommendationProviderTest extends InstrumentationTestCase {
        assertSame(result, capturedBundle.getParcelable(EXTRA_RECOMMENDATION_RESULT));
    }

    @SmallTest
    @Test
    public void testResultCallbackOnResult_runTwice_throwsException() throws Exception {
        final int sequence = 100;
        final NetworkRecommendationProvider.ResultCallback callback =
@@ -104,7 +121,7 @@ public class NetworkRecommendationProviderTest extends InstrumentationTestCase {
        }
    }

    @MediumTest
    @Test
    public void testScoreRequestReceived() throws Exception {
        mStub.requestScores(mTestNetworkKeys);

@@ -114,7 +131,7 @@ public class NetworkRecommendationProviderTest extends InstrumentationTestCase {
        assertSame(mTestNetworkKeys, mRecProvider.mCapturedNetworks);
    }

    @MediumTest
    @Test
    public void testScoreRequest_nullInput() throws Exception {
        mStub.requestScores(null);

@@ -122,7 +139,7 @@ public class NetworkRecommendationProviderTest extends InstrumentationTestCase {
        assertFalse(mScoreRequestLatch.await(200, TimeUnit.MILLISECONDS));
    }

    @MediumTest
    @Test
    public void testScoreRequest_emptyInput() throws Exception {
        mStub.requestScores(new NetworkKey[0]);

@@ -130,6 +147,20 @@ public class NetworkRecommendationProviderTest extends InstrumentationTestCase {
        assertFalse(mScoreRequestLatch.await(200, TimeUnit.MILLISECONDS));
    }

    @Test
    public void testScoreRequest_permissionsEnforced() throws Exception {
        Mockito.doThrow(new SecurityException())
                .when(mContext)
                .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES), anyString());

        try {
            mStub.requestScores(mTestNetworkKeys);
            fail("SecurityException expected.");
        } catch (SecurityException e) {
            // expected
        }
    }

    private static class NetworkRecProvider extends NetworkRecommendationProvider {
        private final CountDownLatch mRecRequestLatch;
        private final CountDownLatch mScoreRequestLatch;
@@ -137,9 +168,9 @@ public class NetworkRecommendationProviderTest extends InstrumentationTestCase {
        ResultCallback mCapturedCallback;
        NetworkKey[] mCapturedNetworks;

        NetworkRecProvider(Handler handler, CountDownLatch recRequestLatch,
        NetworkRecProvider(Context context, Executor executor, CountDownLatch recRequestLatch,
            CountDownLatch networkRequestLatch) {
            super(handler);
            super(context, executor);
            mRecRequestLatch = recRequestLatch;
            mScoreRequestLatch = networkRequestLatch;
        }