Merge "Make the onRequestRecommendation() method async."
This commit is contained in:
@@ -25609,11 +25609,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);
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user