Merge changes I77462359,I107956bf into main
* changes:
Remove broadcast intents from FileChannelInterProcessLockTest
Rework FileChannelInterProcessLockTest to bind to service
diff --git a/tests/tests/libcorefileio/AndroidManifest.xml b/tests/tests/libcorefileio/AndroidManifest.xml
index c6a95b5..4e0ad52 100644
--- a/tests/tests/libcorefileio/AndroidManifest.xml
+++ b/tests/tests/libcorefileio/AndroidManifest.xml
@@ -22,18 +22,11 @@
<application>
<uses-library android:name="android.test.runner"/>
+ <!-- Use ':' to indicate that lockHoldingService needs to run in a separate process.
+ See https://developer.android.com/guide/topics/manifest/service-element#proc -->
<service android:name="android.cts.LockHoldingService"
android:process=":lockHoldingService"
android:permission="android.permission.WRITE_EXTERNAL_STORAGE"/>
- <receiver android:name="android.cts.FileChannelInterProcessLockTest$IntentReceiver"
- android:exported="true">
-
- <intent-filter>
- <action android:name="android.cts.CtsLibcoreFileIOTestCases">
- </action>
- </intent-filter>
-
- </receiver>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/libcorefileio/src/android/cts/FileChannelInterProcessLockTest.java b/tests/tests/libcorefileio/src/android/cts/FileChannelInterProcessLockTest.java
index d949250..bc990e2 100644
--- a/tests/tests/libcorefileio/src/android/cts/FileChannelInterProcessLockTest.java
+++ b/tests/tests/libcorefileio/src/android/cts/FileChannelInterProcessLockTest.java
@@ -16,12 +16,19 @@
package android.cts;
-import android.content.BroadcastReceiver;
+import static java.util.concurrent.TimeUnit.SECONDS;
+
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.os.Bundle;
-import android.os.Debug;
import android.os.Environment;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
import android.test.AndroidTestCase;
import java.io.File;
@@ -33,8 +40,6 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
-import static java.util.concurrent.TimeUnit.SECONDS;
-
@SuppressWarnings("deprecation")
public class FileChannelInterProcessLockTest extends AndroidTestCase {
@@ -55,11 +60,11 @@
* the service. This provides ample amount of time for the service to receive the request from
* the test, then act, and respond back.
*/
- final static int MAX_WAIT_TIME = 20;
+ final static int MAX_WAIT_TIME = 10;
@Override
public void tearDown() throws Exception {
- stopService();
+ IpcChannel.unbindService();
super.tearDown();
}
@@ -419,15 +424,12 @@
ChannelType localChannelType, ChannelType remoteChannelType,
boolean expectToGetLock) throws Exception {
try {
- IntentReceiver.resetReceiverState();
-
// Request that the remote lock be obtained.
- getContext().startService(new Intent(getContext(), LockHoldingService.class)
- .putExtra(LockHoldingService.LOCK_TYPE_KEY, remoteLockType)
- .putExtra(LockHoldingService.CHANNEL_TYPE_KEY, remoteChannelType));
+ IpcChannel.bindService(getContext());
+ IpcChannel.requestRemoteLock(remoteLockType, remoteChannelType);
// Wait for a signal that the remote lock is definitely held.
- assertTrue(IntentReceiver.lockHeldLatch.await(MAX_WAIT_TIME, SECONDS));
+ assertTrue(IpcChannel.lockHeldLatch.await(MAX_WAIT_TIME, SECONDS));
// Try to acquire the local lock in all cases and check whether it could be acquired or
// not as expected.
@@ -440,7 +442,7 @@
}
// Release the remote lock.
} finally {
- stopService();
+ IpcChannel.unbindService();
}
}
@@ -458,30 +460,24 @@
ChannelType localChannelType, ChannelType remoteChannelType,
boolean expectToWait) throws Exception {
try {
- IntentReceiver.resetReceiverState();
-
// The amount of time the remote service should hold lock.
- long remoteLockHoldTimeMillis = 10000;
+ long remoteLockHoldTimeMillis = 5000;
// The amount of time test should get to try to acquire the lock.
long sufficientOverlappingTimeInMillis = 2000;
// This is the allowable delta in the time between the time recorded after the service
// released the lock and the time recorded after the test obtained the lock.
- long lockReleasedAndReacquiredTimeDeltaInMillis = 1000;
+ long lockReleasedAndReacquiredTimeDeltaInMillis = 500;
// Tell the service to acquire a remote lock.
- Intent sendIntent = new Intent(getContext(), LockHoldingService.class)
- .putExtra(LockHoldingService.TIME_TO_HOLD_LOCK_KEY, remoteLockHoldTimeMillis)
- .putExtra(LockHoldingService.LOCK_TYPE_KEY, remoteLockType)
- .putExtra(LockHoldingService.CHANNEL_TYPE_KEY, remoteChannelType)
- .putExtra(LockHoldingService.LOCK_BEHAVIOR_RELEASE_AND_NOTIFY_KEY, true);
-
- getContext().startService(sendIntent);
+ IpcChannel.bindService(getContext());
+ IpcChannel.requestRemoteLockAndRelease(remoteLockType,
+ remoteChannelType, remoteLockHoldTimeMillis);
// Wait for the service to hold the lock and notify for the same.
assertTrue("No remote lock held notification",
- IntentReceiver.lockHeldLatch.await(MAX_WAIT_TIME, SECONDS));
+ IpcChannel.lockHeldLatch.await(MAX_WAIT_TIME, SECONDS));
long localLockNotObtainedTime = System.currentTimeMillis();
@@ -491,9 +487,9 @@
// Wait until the remote lock has definitely been released.
assertTrue("No remote lock release notification",
- IntentReceiver.lockReleasedLatch.await(MAX_WAIT_TIME, SECONDS));
+ IpcChannel.lockReleasedLatch.await(MAX_WAIT_TIME, SECONDS));
- Bundle remoteLockReleasedBundle = IntentReceiver.lockReleasedBundle;
+ Bundle remoteLockReleasedBundle = IpcChannel.lockReleasedBundle;
long remoteLockNotReleasedTime =
remoteLockReleasedBundle.getLong(LockHoldingService.LOCK_NOT_YET_RELEASED_TIMESTAMP);
long remoteLockReleasedTime =
@@ -544,24 +540,10 @@
// Asserting if the fileLock is valid.
assertTrue(fileLock.isValid());
} finally {
- stopService();
+ IpcChannel.unbindService();
}
}
- /**
- * Requests and waits for the service to stop
- */
- void stopService() throws Exception {
- getContext().stopService(new Intent(getContext(), LockHoldingService.class));
- // onStopLatch can be null if we never start the service, possibly because of
- // an earlier failure in the test.
- if (IntentReceiver.onStopLatch != null) {
- assertTrue(IntentReceiver.onStopLatch.await(MAX_WAIT_TIME, SECONDS));
- }
-
- deleteDir(getContext());
- }
-
static enum LockType {
/** Equivalent to {@code tryLock()} */
@@ -747,57 +729,136 @@
}
/**
- * Listens to broadcasts sent by the LockHoldingService and records information / provides
- * latches so the test code can synchronize until it is informed the service has acted on
- * requests it has sent.
+ * Handles the comms with the LockHoldingService.
+ *
+ * Binds to the service and sends requests and receives callbacks from it.
+ *
+ * It records information / provides latches so the test code can synchronize until it is
+ * informed the service has acted on requests it has sent.
*/
- public static class IntentReceiver extends BroadcastReceiver {
+ public static class IpcChannel {
- static CountDownLatch onStartLatch;
+ static Context context;
- static CountDownLatch onStopLatch;
-
+ static CountDownLatch onBindLatch;
+ static CountDownLatch onUnbindLatch;
static CountDownLatch lockHeldLatch;
-
static volatile Bundle lockHeldBundle;
-
static CountDownLatch lockReleasedLatch;
-
static volatile Bundle lockReleasedBundle;
+ static volatile boolean bound = false;
+ static Messenger messenger;
+ static Messenger responseMessenger;
+ static ServiceConnection serviceConnection;
+
+ static Handler responseMessageHandler;
+
+ private static Context getContext() {
+ return context;
+ }
+
/**
- * Reset the IntentReceiver for a new test. Assumes no intents will be received from prior
- * tests.
+ * Reset the IpcChannel for a new test. Assumes no intents will be received from prior
+ * tests.
*/
- public static synchronized void resetReceiverState() {
- onStartLatch = new CountDownLatch(1);
- onStopLatch = new CountDownLatch(1);
+ static synchronized void resetReceiverState(Context testContext) {
+ context = testContext;
+ bound = false;
+ onBindLatch = new CountDownLatch(1);
+ onUnbindLatch = new CountDownLatch(1);
lockHeldLatch = new CountDownLatch(1);
lockReleasedLatch = new CountDownLatch(1);
lockHeldBundle = null;
lockReleasedBundle = null;
+
+ responseMessageHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ Bundle bundle = msg.getData();
+ if (bundle.getBoolean(LockHoldingService.NOTIFICATION_LOCK_HELD)) {
+ lockHeldBundle = bundle;
+ lockHeldLatch.countDown();
+ } else if (bundle.getBoolean(LockHoldingService.NOTIFICATION_LOCK_RELEASED)) {
+ lockReleasedBundle = bundle;
+ lockReleasedLatch.countDown();
+ } else if (bundle.getBoolean(LockHoldingService.NOTIFICATION_READY_FOR_SHUTDOWN)) {
+ onUnbindLatch.countDown();
+ } else {
+ super.handleMessage(msg);
+ }
+ }
+ };
+ responseMessenger = new Messenger(responseMessageHandler);
+
+ serviceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ messenger = new Messenger(service);
+ bound = true;
+ onBindLatch.countDown();
+ }
+
+ @Override
+ public void onBindingDied(ComponentName className) {
+ getContext().unbindService(this);
+ bound = false;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ getContext().unbindService(this);
+ bound = false;
+ }
+ };
}
- @Override
- public void onReceive(Context context, Intent intent) {
- String msg = intent.getStringExtra(LockHoldingService.NOTIFICATION_KEY);
- switch (msg) {
- case LockHoldingService.NOTIFICATION_START:
- onStartLatch.countDown();
- break;
- case LockHoldingService.NOTIFICATION_STOP:
- onStopLatch.countDown();
- break;
- case LockHoldingService.NOTIFICATION_LOCK_HELD:
- lockHeldBundle = intent.getExtras();
- lockHeldLatch.countDown();
- break;
- case LockHoldingService.NOTIFICATION_LOCK_RELEASED:
- lockReleasedBundle = intent.getExtras();
- lockReleasedLatch.countDown();
- break;
+ static void bindService(Context testContext) throws Exception {
+ resetReceiverState(testContext);
+ getContext().bindService(new Intent(getContext(), LockHoldingService.class),
+ serviceConnection, Context.BIND_AUTO_CREATE);
+ assertTrue(onBindLatch.await(MAX_WAIT_TIME, SECONDS));
+ }
+
+ static void requestRemoteLock(LockType lockType, ChannelType channelType) throws Exception {
+ Message msg = Message.obtain(null,
+ LockHoldingService.LOCK_BEHAVIOUR_ACQUIRE_ONLY_AND_NOTIFY, 0, 0);
+ Bundle bundle = msg.getData();
+ bundle.putSerializable(LockHoldingService.LOCK_TYPE_KEY, lockType);
+ bundle.putSerializable(LockHoldingService.CHANNEL_TYPE_KEY, channelType);
+ msg.replyTo = responseMessenger;
+ messenger.send(msg);
+ }
+
+ static void requestRemoteLockAndRelease(LockType lockType, ChannelType channelType,
+ long lockHoldTimeMillis) throws Exception {
+ Message msg = Message.obtain(null,
+ LockHoldingService.LOCK_BEHAVIOR_RELEASE_AND_NOTIFY, 0, 0);
+ Bundle bundle = msg.getData();
+ bundle.putSerializable(LockHoldingService.LOCK_TYPE_KEY, lockType);
+ bundle.putSerializable(LockHoldingService.CHANNEL_TYPE_KEY, channelType);
+ bundle.putLong(LockHoldingService.TIME_TO_HOLD_LOCK_KEY, lockHoldTimeMillis);
+ msg.replyTo = responseMessenger;
+ messenger.send(msg);
+ }
+
+ /**
+ * Requests and waits for the service to stop
+ */
+ static void unbindService() throws Exception {
+ if (bound) {
+ Message msg = Message.obtain(null, LockHoldingService.PREPARE_FOR_SHUTDOWN, 0, 0);
+ msg.replyTo = responseMessenger;
+ messenger.send(msg);
+
+ assertTrue(onUnbindLatch.await(MAX_WAIT_TIME, SECONDS));
+ getContext().unbindService(serviceConnection);
+ bound = false;
}
+ messenger = null;
+ deleteDir(getContext());
}
}
+
}
diff --git a/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java b/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java
index 0df8846..6182235 100644
--- a/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java
+++ b/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java
@@ -16,18 +16,23 @@
package android.cts;
+import static android.cts.FileChannelInterProcessLockTest.ChannelType;
+import static android.cts.FileChannelInterProcessLockTest.LockType;
+
import android.app.Service;
import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
import android.util.Log;
import java.io.IOException;
import java.nio.channels.FileLock;
import java.util.concurrent.ExecutionException;
-import static android.cts.FileChannelInterProcessLockTest.ChannelType;
-import static android.cts.FileChannelInterProcessLockTest.LockType;
-
/**
* A Service that listens for commands from the FileChannelInterProcessLockTest to acquire locks of
* different types. It exists to test the behavior when file locks are acquired/released across
@@ -51,16 +56,6 @@
static final String NOTIFICATION_KEY = "notification";
/**
- * The value for the notification sent to the test after the service starts.
- */
- static final String NOTIFICATION_START = "onStart";
-
- /**
- * The value for the notification sent to the test just before the service stops.
- */
- static final String NOTIFICATION_STOP = "onStop";
-
- /**
* The value for the notification sent to the test after the lock is acquired.
*/
static final String NOTIFICATION_LOCK_HELD = "lockHeld";
@@ -71,6 +66,11 @@
static final String NOTIFICATION_LOCK_RELEASED = "lockReleased";
/**
+ * The value for the notification sent to the test after the lock is released for shutdown
+ */
+ static final String NOTIFICATION_READY_FOR_SHUTDOWN = "readyForShutdown";
+
+ /**
* The key of the Bundle extra used to send time for which the service should wait before
* releasing the lock.
*/
@@ -87,72 +87,86 @@
static final String CHANNEL_TYPE_KEY = "channelType";
/**
- * The key of the Bundle extra used to let he service know whether to release the lock after
- * some time.
+ * The message code used to let he service know to release the lock after some time.
*/
- static final String LOCK_BEHAVIOR_RELEASE_AND_NOTIFY_KEY = "releaseAndNotify";
+ static final int LOCK_BEHAVIOR_RELEASE_AND_NOTIFY = 1;
- static final String ACTION_TYPE_FOR_INTENT_COMMUNICATION
- = "android.cts.CtsLibcoreFileIOTestCases";
+ /**
+ * The message code used to let he service know to lock without releasing.
+ */
+ static final int LOCK_BEHAVIOUR_ACQUIRE_ONLY_AND_NOTIFY = 2;
+
+ /**
+ * The message code used to let the service know to release the lock before test end, if still
+ * held.
+ */
+ static final int PREPARE_FOR_SHUTDOWN = 3;
final String LOG_MESSAGE_TAG = "CtsLibcoreFileIOTestCases";
private FileLock fileLock = null;
- public IBinder onBind(Intent intent) {
- return null;
+ private class LockHoldingHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ try {
+ switch (msg.what) {
+ case LOCK_BEHAVIOR_RELEASE_AND_NOTIFY:
+ acquireLockAndThenWaitThenRelease(msg);
+ break;
+ case LOCK_BEHAVIOUR_ACQUIRE_ONLY_AND_NOTIFY:
+ acquireLock(msg);
+ break;
+ case PREPARE_FOR_SHUTDOWN:
+ prepareForShutdown(msg);
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ } catch (Exception e) {
+ Log.e(LOG_MESSAGE_TAG, "Exception acquire lock", e);
+ }
+ }
}
- @Override
- public int onStartCommand(Intent intent, int flags, int startID) {
- try {
- if (intent.getBooleanExtra(LOCK_BEHAVIOR_RELEASE_AND_NOTIFY_KEY, false)) {
- acquireLockAndThenWaitThenRelease(intent);
- } else {
- acquireLock(intent);
- }
- } catch (Exception e) {
- Log.e(LOG_MESSAGE_TAG, "Exception acquire lock", e);
- }
- return START_STICKY;
+ private Messenger messenger;
+
+ public IBinder onBind(Intent intent) {
+ messenger = new Messenger(new LockHoldingHandler());
+ return messenger.getBinder();
}
/**
* Acquires the lock asked by the test indefinitely.
*/
- private void acquireLock(Intent intent) throws IOException,
- InterruptedException, ExecutionException {
- LockType lockType = (LockType) intent.getSerializableExtra(LOCK_TYPE_KEY);
- ChannelType channelType = (ChannelType) intent.getSerializableExtra(CHANNEL_TYPE_KEY);
+ private void acquireLock(Message msg) throws IOException,
+ InterruptedException, ExecutionException, RemoteException {
+ Bundle bundle = msg.getData();
+ LockType lockType = (LockType) bundle.get(LOCK_TYPE_KEY);
+ ChannelType channelType = (ChannelType) bundle.get(CHANNEL_TYPE_KEY);
// Acquire the lock based on the information contained in the intent received.
this.fileLock = FileChannelInterProcessLockTest.acquire(this, lockType, channelType);
- Intent responseIntent = new Intent()
- .setPackage("android.libcorefileio.cts")
- .putExtra(NOTIFICATION_KEY, NOTIFICATION_LOCK_HELD)
- .setAction(ACTION_TYPE_FOR_INTENT_COMMUNICATION);
- sendBroadcast(responseIntent);
+
+ notifyLockHeld(msg);
}
/**
- * Acquires and holds the lock for a time specified by the test. Sends a broadcast message after
+ * Acquires and holds the lock for a time specified by the test. Sends a response message after
* releasing the lock.
*/
- private void acquireLockAndThenWaitThenRelease(Intent intent)
- throws IOException, InterruptedException, ExecutionException {
- long lockHoldTimeMillis = intent.getLongExtra(TIME_TO_HOLD_LOCK_KEY, 0);
+ private void acquireLockAndThenWaitThenRelease(Message msg)
+ throws IOException, InterruptedException, ExecutionException, RemoteException {
+ Bundle bundle = msg.getData();
+ long lockHoldTimeMillis = bundle.getLong(TIME_TO_HOLD_LOCK_KEY, 0);
+ LockType lockType = (LockType) bundle.get(LOCK_TYPE_KEY);
+ ChannelType channelType = (ChannelType) bundle.get(CHANNEL_TYPE_KEY);
// Acquire the lock.
- LockType lockType = (LockType) intent.getSerializableExtra(LOCK_TYPE_KEY);
- ChannelType channelType = (ChannelType) intent.getSerializableExtra(CHANNEL_TYPE_KEY);
this.fileLock = FileChannelInterProcessLockTest.acquire(this, lockType, channelType);
// Signal the lock is now held.
- Intent heldIntent = new Intent()
- .setPackage("android.libcorefileio.cts")
- .putExtra(NOTIFICATION_KEY, NOTIFICATION_LOCK_HELD)
- .setAction(ACTION_TYPE_FOR_INTENT_COMMUNICATION);
- sendBroadcast(heldIntent);
+ notifyLockHeld(msg);
Thread.sleep(lockHoldTimeMillis);
@@ -164,17 +178,35 @@
long lockReleasedTimestamp = System.currentTimeMillis();
// Signal the lock is released and some information about timing.
- Intent releaseIntent = new Intent()
- .setPackage("android.libcorefileio.cts")
- .putExtra(NOTIFICATION_KEY, NOTIFICATION_LOCK_RELEASED)
- .putExtra(LOCK_NOT_YET_RELEASED_TIMESTAMP, lockNotReleasedTimestamp)
- .putExtra(LOCK_DEFINITELY_RELEASED_TIMESTAMP, lockReleasedTimestamp)
- .setAction(ACTION_TYPE_FOR_INTENT_COMMUNICATION);
- sendBroadcast(releaseIntent);
+ notifyLockReleased(msg, lockNotReleasedTimestamp, lockReleasedTimestamp);
}
- @Override
- public void onDestroy() {
+ private void notifyLockHeld(Message msg) throws RemoteException {
+ Message rsp = msg.obtain();
+ Bundle rspBundle = rsp.getData();
+ rspBundle.putBoolean(NOTIFICATION_LOCK_HELD, true);
+ msg.replyTo.send(rsp);
+ }
+
+ private void notifyLockReleased(Message msg, long lockNotReleasedTimestamp,
+ long lockReleasedTimestamp) throws RemoteException {
+ Message rsp = msg.obtain();
+ Bundle rspBundle = rsp.getData();
+ rspBundle.putBoolean(NOTIFICATION_LOCK_RELEASED, true);
+ rspBundle.putLong(LOCK_NOT_YET_RELEASED_TIMESTAMP, lockNotReleasedTimestamp);
+ rspBundle.putLong(LOCK_DEFINITELY_RELEASED_TIMESTAMP, lockReleasedTimestamp);
+ msg.replyTo.send(rsp);
+ }
+
+ private void prepareForShutdown(Message msg) throws RemoteException {
+ doReleaseLock();
+ Message rsp = msg.obtain();
+ Bundle rspBundle = rsp.getData();
+ rspBundle.putBoolean(NOTIFICATION_READY_FOR_SHUTDOWN, true);
+ msg.replyTo.send(rsp);
+ }
+
+ private void doReleaseLock() {
try {
if (fileLock != null) {
fileLock.release();
@@ -182,10 +214,11 @@
} catch (IOException e) {
Log.e(LOG_MESSAGE_TAG, e.getMessage());
}
- Intent intent = new Intent()
- .setPackage("android.libcorefileio.cts")
- .putExtra(NOTIFICATION_KEY, NOTIFICATION_STOP)
- .setAction(ACTION_TYPE_FOR_INTENT_COMMUNICATION);
- sendBroadcast(intent);
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ doReleaseLock();
+ return false;
}
}