Merge "Make the onRequestRecommendation() method async." am: fbeacb02c0
am: a6c6bc333d
Change-Id: I56a830273f8e3246b598c2ea7a027b87e3066e16
This commit is contained in:
@@ -25605,11 +25605,15 @@ package android.net {
|
|||||||
public abstract class NetworkRecommendationProvider {
|
public abstract class NetworkRecommendationProvider {
|
||||||
ctor public NetworkRecommendationProvider(android.os.Handler);
|
ctor public NetworkRecommendationProvider(android.os.Handler);
|
||||||
method public final android.os.IBinder getBinder();
|
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_RECOMMENDATION_RESULT = "android.net.extra.RECOMMENDATION_RESULT";
|
||||||
field public static final java.lang.String EXTRA_SEQUENCE = "android.net.extra.SEQUENCE";
|
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 {
|
public class NetworkRequest implements android.os.Parcelable {
|
||||||
method public int describeContents();
|
method public int describeContents();
|
||||||
method public void writeToParcel(android.os.Parcel, int);
|
method public void writeToParcel(android.os.Parcel, int);
|
||||||
|
|||||||
@@ -10,6 +10,11 @@ import android.os.Message;
|
|||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
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.
|
* The base class for implementing a network recommendation provider.
|
||||||
* @hide
|
* @hide
|
||||||
@@ -42,11 +47,12 @@ public abstract class NetworkRecommendationProvider {
|
|||||||
*
|
*
|
||||||
* @param request a {@link RecommendationRequest} instance containing additional
|
* @param request a {@link RecommendationRequest} instance containing additional
|
||||||
* request details
|
* request details
|
||||||
* @return a {@link RecommendationResult} instance containing the recommended
|
* @param callback a {@link ResultCallback} instance. When a {@link RecommendationResult} is
|
||||||
* network to connect to
|
* 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
|
* Services that can handle {@link NetworkScoreManager#ACTION_RECOMMEND_NETWORKS} should
|
||||||
@@ -56,6 +62,60 @@ public abstract class NetworkRecommendationProvider {
|
|||||||
return mService;
|
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 {
|
private final class ServiceHandler extends Handler {
|
||||||
static final int MSG_GET_RECOMMENDATION = 1;
|
static final int MSG_GET_RECOMMENDATION = 1;
|
||||||
|
|
||||||
@@ -72,16 +132,8 @@ public abstract class NetworkRecommendationProvider {
|
|||||||
final int seq = msg.arg1;
|
final int seq = msg.arg1;
|
||||||
final RecommendationRequest request =
|
final RecommendationRequest request =
|
||||||
msg.getData().getParcelable(EXTRA_RECOMMENDATION_REQUEST);
|
msg.getData().getParcelable(EXTRA_RECOMMENDATION_REQUEST);
|
||||||
final RecommendationResult result = onRequestRecommendation(request);
|
final ResultCallback resultCallback = new ResultCallback(callback, seq);
|
||||||
final Bundle data = new Bundle();
|
onRequestRecommendation(request, resultCallback);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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