Loading api/system-current.txt +5 −1 Original line number Diff line number Diff line Loading @@ -25998,11 +25998,15 @@ package android.net { public abstract class NetworkRecommendationProvider { ctor public NetworkRecommendationProvider(android.os.Handler); method public final android.os.IBinder getBinder(); method public abstract android.net.RecommendationResult onRequestRecommendation(android.net.RecommendationRequest); method public abstract void onRequestRecommendation(android.net.RecommendationRequest, android.net.NetworkRecommendationProvider.ResultCallback); field public static final java.lang.String EXTRA_RECOMMENDATION_RESULT = "android.net.extra.RECOMMENDATION_RESULT"; field public static final java.lang.String EXTRA_SEQUENCE = "android.net.extra.SEQUENCE"; } public static final class NetworkRecommendationProvider.ResultCallback { method public void onResult(android.net.RecommendationResult); } public class NetworkRequest implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); core/java/android/net/NetworkRecommendationProvider.java +66 −14 Original line number Diff line number Diff line Loading @@ -10,6 +10,11 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; /** * The base class for implementing a network recommendation provider. * @hide Loading Loading @@ -42,11 +47,12 @@ public abstract class NetworkRecommendationProvider { * * @param request a {@link RecommendationRequest} instance containing additional * request details * @return a {@link RecommendationResult} instance containing the recommended * network to connect to * @param callback a {@link ResultCallback} instance. When a {@link RecommendationResult} is * available it must be passed into * {@link ResultCallback#onResult(RecommendationResult)}. */ public abstract RecommendationResult onRequestRecommendation(RecommendationRequest request); public abstract void onRequestRecommendation(RecommendationRequest request, ResultCallback callback); /** * Services that can handle {@link NetworkScoreManager#ACTION_RECOMMEND_NETWORKS} should Loading @@ -56,6 +62,60 @@ public abstract class NetworkRecommendationProvider { return mService; } /** * A callback implementing applications should invoke when a {@link RecommendationResult} * is available. */ public static final class ResultCallback { private final IRemoteCallback mCallback; private final int mSequence; private final AtomicBoolean mCallbackRun; /** * @hide */ @VisibleForTesting public ResultCallback(IRemoteCallback callback, int sequence) { mCallback = callback; mSequence = sequence; mCallbackRun = new AtomicBoolean(false); } /** * Run the callback with the available {@link RecommendationResult}. * @param result a {@link RecommendationResult} instance. */ public void onResult(RecommendationResult result) { if (!mCallbackRun.compareAndSet(false, true)) { throw new IllegalStateException("The callback cannot be run more than once."); } final Bundle data = new Bundle(); data.putInt(EXTRA_SEQUENCE, mSequence); data.putParcelable(EXTRA_RECOMMENDATION_RESULT, result); try { mCallback.sendResult(data); } catch (RemoteException e) { Log.w(TAG, "Callback failed for seq: " + mSequence, e); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ResultCallback that = (ResultCallback) o; return mSequence == that.mSequence && Objects.equals(mCallback, that.mCallback); } @Override public int hashCode() { return Objects.hash(mCallback, mSequence); } } private final class ServiceHandler extends Handler { static final int MSG_GET_RECOMMENDATION = 1; Loading @@ -72,16 +132,8 @@ public abstract class NetworkRecommendationProvider { final int seq = msg.arg1; final RecommendationRequest request = msg.getData().getParcelable(EXTRA_RECOMMENDATION_REQUEST); final RecommendationResult result = onRequestRecommendation(request); final Bundle data = new Bundle(); data.putInt(EXTRA_SEQUENCE, seq); data.putParcelable(EXTRA_RECOMMENDATION_RESULT, result); try { callback.sendResult(data); } catch (RemoteException e) { Log.w(TAG, "Callback failed for seq: " + seq, e); } final ResultCallback resultCallback = new ResultCallback(callback, seq); onRequestRecommendation(request, resultCallback); break; default: Loading core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java 0 → 100644 +119 −0 Original line number Diff line number Diff line package android.net; import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT; import static android.net.NetworkRecommendationProvider.EXTRA_SEQUENCE; 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 org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Unit test for the {@link NetworkRecommendationProvider}. */ public class NetworkRecommendationProviderTest extends InstrumentationTestCase { @Mock private IRemoteCallback mMockRemoteCallback; private NetworkRecProvider mRecProvider; private Handler mHandler; private INetworkRecommendationProvider mStub; private CountDownLatch mCountDownLatch; @Override 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(); mCountDownLatch = new CountDownLatch(1); mHandler = new Handler(thread.getLooper()); mRecProvider = new NetworkRecProvider(mHandler, mCountDownLatch); mStub = INetworkRecommendationProvider.Stub.asInterface(mRecProvider.getBinder()); } @MediumTest public void testRequestReceived() throws Exception { final RecommendationRequest request = new RecommendationRequest.Builder().build(); final int sequence = 100; mStub.requestRecommendation(request, mMockRemoteCallback, sequence); // wait for onRequestRecommendation() to be called in our impl below. mCountDownLatch.await(200, TimeUnit.MILLISECONDS); NetworkRecommendationProvider.ResultCallback expectedResultCallback = new NetworkRecommendationProvider.ResultCallback(mMockRemoteCallback, sequence); assertEquals(request, mRecProvider.mCapturedRequest); assertEquals(expectedResultCallback, mRecProvider.mCapturedCallback); } @SmallTest public void testResultCallbackOnResult() throws Exception { final int sequence = 100; final NetworkRecommendationProvider.ResultCallback callback = new NetworkRecommendationProvider.ResultCallback(mMockRemoteCallback, sequence); final RecommendationResult result = new RecommendationResult(null); callback.onResult(result); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); Mockito.verify(mMockRemoteCallback).sendResult(bundleCaptor.capture()); Bundle capturedBundle = bundleCaptor.getValue(); assertEquals(sequence, capturedBundle.getInt(EXTRA_SEQUENCE)); assertSame(result, capturedBundle.getParcelable(EXTRA_RECOMMENDATION_RESULT)); } @SmallTest public void testResultCallbackOnResult_runTwice_throwsException() throws Exception { final int sequence = 100; final NetworkRecommendationProvider.ResultCallback callback = new NetworkRecommendationProvider.ResultCallback(mMockRemoteCallback, sequence); final RecommendationResult result = new RecommendationResult(null); callback.onResult(result); try { callback.onResult(result); fail("Callback ran more than once."); } catch (IllegalStateException e) { // expected } } private static class NetworkRecProvider extends NetworkRecommendationProvider { private final CountDownLatch mCountDownLatch; RecommendationRequest mCapturedRequest; ResultCallback mCapturedCallback; NetworkRecProvider(Handler handler, CountDownLatch countDownLatch) { super(handler); mCountDownLatch = countDownLatch; } @Override public void onRequestRecommendation(RecommendationRequest request, ResultCallback callback) { mCapturedRequest = request; mCapturedCallback = callback; mCountDownLatch.countDown(); } } } Loading
api/system-current.txt +5 −1 Original line number Diff line number Diff line Loading @@ -25998,11 +25998,15 @@ package android.net { public abstract class NetworkRecommendationProvider { ctor public NetworkRecommendationProvider(android.os.Handler); method public final android.os.IBinder getBinder(); method public abstract android.net.RecommendationResult onRequestRecommendation(android.net.RecommendationRequest); method public abstract void onRequestRecommendation(android.net.RecommendationRequest, android.net.NetworkRecommendationProvider.ResultCallback); field public static final java.lang.String EXTRA_RECOMMENDATION_RESULT = "android.net.extra.RECOMMENDATION_RESULT"; field public static final java.lang.String EXTRA_SEQUENCE = "android.net.extra.SEQUENCE"; } public static final class NetworkRecommendationProvider.ResultCallback { method public void onResult(android.net.RecommendationResult); } public class NetworkRequest implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int);
core/java/android/net/NetworkRecommendationProvider.java +66 −14 Original line number Diff line number Diff line Loading @@ -10,6 +10,11 @@ import android.os.Message; import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; /** * The base class for implementing a network recommendation provider. * @hide Loading Loading @@ -42,11 +47,12 @@ public abstract class NetworkRecommendationProvider { * * @param request a {@link RecommendationRequest} instance containing additional * request details * @return a {@link RecommendationResult} instance containing the recommended * network to connect to * @param callback a {@link ResultCallback} instance. When a {@link RecommendationResult} is * available it must be passed into * {@link ResultCallback#onResult(RecommendationResult)}. */ public abstract RecommendationResult onRequestRecommendation(RecommendationRequest request); public abstract void onRequestRecommendation(RecommendationRequest request, ResultCallback callback); /** * Services that can handle {@link NetworkScoreManager#ACTION_RECOMMEND_NETWORKS} should Loading @@ -56,6 +62,60 @@ public abstract class NetworkRecommendationProvider { return mService; } /** * A callback implementing applications should invoke when a {@link RecommendationResult} * is available. */ public static final class ResultCallback { private final IRemoteCallback mCallback; private final int mSequence; private final AtomicBoolean mCallbackRun; /** * @hide */ @VisibleForTesting public ResultCallback(IRemoteCallback callback, int sequence) { mCallback = callback; mSequence = sequence; mCallbackRun = new AtomicBoolean(false); } /** * Run the callback with the available {@link RecommendationResult}. * @param result a {@link RecommendationResult} instance. */ public void onResult(RecommendationResult result) { if (!mCallbackRun.compareAndSet(false, true)) { throw new IllegalStateException("The callback cannot be run more than once."); } final Bundle data = new Bundle(); data.putInt(EXTRA_SEQUENCE, mSequence); data.putParcelable(EXTRA_RECOMMENDATION_RESULT, result); try { mCallback.sendResult(data); } catch (RemoteException e) { Log.w(TAG, "Callback failed for seq: " + mSequence, e); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ResultCallback that = (ResultCallback) o; return mSequence == that.mSequence && Objects.equals(mCallback, that.mCallback); } @Override public int hashCode() { return Objects.hash(mCallback, mSequence); } } private final class ServiceHandler extends Handler { static final int MSG_GET_RECOMMENDATION = 1; Loading @@ -72,16 +132,8 @@ public abstract class NetworkRecommendationProvider { final int seq = msg.arg1; final RecommendationRequest request = msg.getData().getParcelable(EXTRA_RECOMMENDATION_REQUEST); final RecommendationResult result = onRequestRecommendation(request); final Bundle data = new Bundle(); data.putInt(EXTRA_SEQUENCE, seq); data.putParcelable(EXTRA_RECOMMENDATION_RESULT, result); try { callback.sendResult(data); } catch (RemoteException e) { Log.w(TAG, "Callback failed for seq: " + seq, e); } final ResultCallback resultCallback = new ResultCallback(callback, seq); onRequestRecommendation(request, resultCallback); break; default: Loading
core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java 0 → 100644 +119 −0 Original line number Diff line number Diff line package android.net; import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT; import static android.net.NetworkRecommendationProvider.EXTRA_SEQUENCE; 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 org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Unit test for the {@link NetworkRecommendationProvider}. */ public class NetworkRecommendationProviderTest extends InstrumentationTestCase { @Mock private IRemoteCallback mMockRemoteCallback; private NetworkRecProvider mRecProvider; private Handler mHandler; private INetworkRecommendationProvider mStub; private CountDownLatch mCountDownLatch; @Override 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(); mCountDownLatch = new CountDownLatch(1); mHandler = new Handler(thread.getLooper()); mRecProvider = new NetworkRecProvider(mHandler, mCountDownLatch); mStub = INetworkRecommendationProvider.Stub.asInterface(mRecProvider.getBinder()); } @MediumTest public void testRequestReceived() throws Exception { final RecommendationRequest request = new RecommendationRequest.Builder().build(); final int sequence = 100; mStub.requestRecommendation(request, mMockRemoteCallback, sequence); // wait for onRequestRecommendation() to be called in our impl below. mCountDownLatch.await(200, TimeUnit.MILLISECONDS); NetworkRecommendationProvider.ResultCallback expectedResultCallback = new NetworkRecommendationProvider.ResultCallback(mMockRemoteCallback, sequence); assertEquals(request, mRecProvider.mCapturedRequest); assertEquals(expectedResultCallback, mRecProvider.mCapturedCallback); } @SmallTest public void testResultCallbackOnResult() throws Exception { final int sequence = 100; final NetworkRecommendationProvider.ResultCallback callback = new NetworkRecommendationProvider.ResultCallback(mMockRemoteCallback, sequence); final RecommendationResult result = new RecommendationResult(null); callback.onResult(result); final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); Mockito.verify(mMockRemoteCallback).sendResult(bundleCaptor.capture()); Bundle capturedBundle = bundleCaptor.getValue(); assertEquals(sequence, capturedBundle.getInt(EXTRA_SEQUENCE)); assertSame(result, capturedBundle.getParcelable(EXTRA_RECOMMENDATION_RESULT)); } @SmallTest public void testResultCallbackOnResult_runTwice_throwsException() throws Exception { final int sequence = 100; final NetworkRecommendationProvider.ResultCallback callback = new NetworkRecommendationProvider.ResultCallback(mMockRemoteCallback, sequence); final RecommendationResult result = new RecommendationResult(null); callback.onResult(result); try { callback.onResult(result); fail("Callback ran more than once."); } catch (IllegalStateException e) { // expected } } private static class NetworkRecProvider extends NetworkRecommendationProvider { private final CountDownLatch mCountDownLatch; RecommendationRequest mCapturedRequest; ResultCallback mCapturedCallback; NetworkRecProvider(Handler handler, CountDownLatch countDownLatch) { super(handler); mCountDownLatch = countDownLatch; } @Override public void onRequestRecommendation(RecommendationRequest request, ResultCallback callback) { mCapturedRequest = request; mCapturedCallback = callback; mCountDownLatch.countDown(); } } }