[P2P] Public API to clean-up resources of P2P

1. Add public API to close a Channel and allow configuration to be
   cleaned-up. Actual clean-up will only happen when the last p2p
   client executes close or terminates (binder death is triggered
   for the service).
2. Add Close Guard to verify that API is called - issue warning
   otherwise. Note that to actually get the warning an app needs
   to use the StrictMode policy:

   StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
       .detectLeakedClosableObjects().build());

Bug: 37443149
Test: (new) unit tests, CtsVerifier, Settings/WFD app
Change-Id: I9590101ef7f7ba0a90812634ac1b931d1482fe72
This commit is contained in:
Etan Cohen
2017-07-06 07:18:28 -07:00
parent 520caf4a75
commit a1dff77196
6 changed files with 136 additions and 24 deletions

View File

@@ -27070,7 +27070,8 @@ package android.net.wifi.p2p {
method public abstract void onSuccess();
}
public static class WifiP2pManager.Channel {
public static class WifiP2pManager.Channel implements java.lang.AutoCloseable {
method public void close();
}
public static abstract interface WifiP2pManager.ChannelListener {

View File

@@ -29866,7 +29866,8 @@ package android.net.wifi.p2p {
method public abstract void onSuccess();
}
public static class WifiP2pManager.Channel {
public static class WifiP2pManager.Channel implements java.lang.AutoCloseable {
method public void close();
}
public static abstract interface WifiP2pManager.ChannelListener {

View File

@@ -27181,7 +27181,8 @@ package android.net.wifi.p2p {
method public abstract void onSuccess();
}
public static class WifiP2pManager.Channel {
public static class WifiP2pManager.Channel implements java.lang.AutoCloseable {
method public void close();
}
public static abstract interface WifiP2pManager.ChannelListener {

View File

@@ -41,6 +41,8 @@ import android.util.Log;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
import dalvik.system.CloseGuard;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -668,15 +670,21 @@ public class WifiP2pManager {
* Most p2p operations require a Channel as an argument. An instance of Channel is obtained
* by doing a call on {@link #initialize}
*/
public static class Channel {
Channel(Context context, Looper looper, ChannelListener l, Binder binder) {
public static class Channel implements AutoCloseable {
/** @hide */
public Channel(Context context, Looper looper, ChannelListener l, Binder binder,
WifiP2pManager p2pManager) {
mAsyncChannel = new AsyncChannel();
mHandler = new P2pHandler(looper);
mChannelListener = l;
mContext = context;
mBinder = binder;
mP2pManager = p2pManager;
mCloseGuard.open("close");
}
private final static int INVALID_LISTENER_KEY = 0;
private final WifiP2pManager mP2pManager;
private ChannelListener mChannelListener;
private ServiceResponseListener mServRspListener;
private DnsSdServiceResponseListener mDnsSdServRspListener;
@@ -686,6 +694,41 @@ public class WifiP2pManager {
private final Object mListenerMapLock = new Object();
private int mListenerKey = 0;
private final CloseGuard mCloseGuard = CloseGuard.get();
/**
* Close the current P2P connection and indicate to the P2P service that connections
* created by the app can be removed.
*/
public void close() {
if (mP2pManager == null) {
Log.w(TAG, "Channel.close(): Null mP2pManager!?");
} else {
try {
mP2pManager.mService.close(mBinder);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
mAsyncChannel.disconnect();
mCloseGuard.close();
}
/** @hide */
@Override
protected void finalize() throws Throwable {
try {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
close();
} finally {
super.finalize();
}
}
/* package */ final Binder mBinder;
private AsyncChannel mAsyncChannel;
@@ -913,11 +956,12 @@ public class WifiP2pManager {
Messenger messenger, Binder binder) {
if (messenger == null) return null;
Channel c = new Channel(srcContext, srcLooper, listener, binder);
Channel c = new Channel(srcContext, srcLooper, listener, binder, this);
if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger)
== AsyncChannel.STATUS_SUCCESSFUL) {
return c;
} else {
c.close();
return null;
}
}
@@ -1421,24 +1465,6 @@ public class WifiP2pManager {
}
}
/**
* Close the current P2P connection and clean-up any configuration requested by the
* current app. Takes same action as taken when the app dies.
*
* @param c is the channel created at {@link #initialize}
*
* @hide
*/
public void close(Channel c) {
try {
if (c != null) {
mService.close(c.mBinder);
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Get a handover request message for use in WFA NFC Handover transfer.
* @hide

View File

@@ -50,6 +50,7 @@ LOCAL_JACK_COVERAGE_EXCLUDE_FILTER := $(jacoco_exclude)
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
core-test-rules \
guava \
mockito-target-minus-junit4 \
frameworks-base-testutils \

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net.wifi.p2p;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.os.test.TestLooper;
import libcore.junit.util.ResourceLeakageDetector;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Unit test harness for WifiP2pManager.
*/
public class WifiP2pManagerTest {
private WifiP2pManager mDut;
private TestLooper mTestLooper;
@Mock
public Context mContextMock;
@Mock
IWifiP2pManager mP2pServiceMock;
@Rule
public ResourceLeakageDetector.LeakageDetectorRule leakageDetectorRule =
ResourceLeakageDetector.getRule();
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mDut = new WifiP2pManager(mP2pServiceMock);
mTestLooper = new TestLooper();
}
/**
* Validate that on finalize we close the channel and flag a resource leakage.
*/
@Test
public void testChannelFinalize() throws Exception {
WifiP2pManager.Channel channel = new WifiP2pManager.Channel(mContextMock,
mTestLooper.getLooper(), null, null, mDut);
leakageDetectorRule.assertUnreleasedResourceCount(channel, 1);
}
/**
* Validate that when close is called on a channel it frees up resources (i.e. don't
* get flagged again on finalize).
*/
@Test
public void testChannelClose() throws Exception {
WifiP2pManager.Channel channel = new WifiP2pManager.Channel(mContextMock,
mTestLooper.getLooper(), null, null, mDut);
channel.close();
verify(mP2pServiceMock).close(any());
leakageDetectorRule.assertUnreleasedResourceCount(channel, 0);
}
}