diff options
author | Shubham Ajmera <shubhamajmera@google.com> | 2016-05-13 08:34:08 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2016-05-13 08:34:08 +0000 |
commit | 7c116469192d5705de101bd7513a6c9ad52cbdca (patch) | |
tree | 4a900f20acaaf7aee1ec44177b31f7dc09034278 | |
parent | a40d072b792057929097e2fbadf01d341a6a5456 (diff) | |
parent | 34c10c16614ff6d4a5e2b50aeb86ed1d1fc603ec (diff) | |
download | platform_cts-7c116469192d5705de101bd7513a6c9ad52cbdca.tar.gz platform_cts-7c116469192d5705de101bd7513a6c9ad52cbdca.tar.bz2 platform_cts-7c116469192d5705de101bd7513a6c9ad52cbdca.zip |
Merge "Add tests for java.nio.channels.FileChannel lock methods"android-wear-n-preview-1android-n-preview-3
6 files changed, 680 insertions, 141 deletions
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk index 745cd6bae6c..7a2ced28a05 100644 --- a/CtsTestCaseList.mk +++ b/CtsTestCaseList.mk @@ -172,6 +172,7 @@ cts_test_packages := \ CtsJniTestCases \ CtsKeystoreTestCases \ CtsLibcoreLegacy22TestCases \ + CtsLibcoreFileIOTestCases \ CtsLocationTestCases \ CtsLocation2TestCases \ CtsMediaStressTestCases \ diff --git a/tests/tests/libcorefileio/AndroidManifest.xml b/tests/tests/libcorefileio/AndroidManifest.xml index 4557eec457c..6eb1439b100 100644 --- a/tests/tests/libcorefileio/AndroidManifest.xml +++ b/tests/tests/libcorefileio/AndroidManifest.xml @@ -26,7 +26,7 @@ android:process=":lockHoldingService" android:permission="android.permission.WRITE_EXTERNAL_STORAGE" /> - <receiver android:name="android.cts.FileChannelTryLockTest$IntentReceiver"> + <receiver android:name="android.cts.FileChannelInterProcessLockTest$IntentReceiver"> <intent-filter> <action android:name="android.cts.CtsLibcoreFileIOTestCases"> @@ -43,4 +43,3 @@ </instrumentation> </manifest> - diff --git a/tests/tests/libcorefileio/AndroidTest.xml b/tests/tests/libcorefileio/AndroidTest.xml deleted file mode 100644 index 9baa7132b72..00000000000 --- a/tests/tests/libcorefileio/AndroidTest.xml +++ /dev/null @@ -1,23 +0,0 @@ -<!-- Copyright (C) 2016 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. ---> -<configuration description="Config for CTS Legacy Libcore test cases"> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="test-file-name" value="CtsLibcoreFileIOTestCases.apk" /> - </target_preparer> - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="android.libcorefileio.cts" /> - </test> -</configuration>
\ No newline at end of file diff --git a/tests/tests/libcorefileio/src/android/cts/FileChannelInterProcessLockTest.java b/tests/tests/libcorefileio/src/android/cts/FileChannelInterProcessLockTest.java new file mode 100644 index 00000000000..05894d5f1c8 --- /dev/null +++ b/tests/tests/libcorefileio/src/android/cts/FileChannelInterProcessLockTest.java @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2016 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.cts; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Environment; +import android.test.AndroidTestCase; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileLock; +import java.util.concurrent.CountDownLatch; + +import static java.util.concurrent.TimeUnit.SECONDS; + +@SuppressWarnings("deprecation") +public class FileChannelInterProcessLockTest extends AndroidTestCase { + + /** The directory where file locks are created */ + final static String DIR_NAME = "CtsFileIOTest"; + + /** The name of the file used when acquiring a lock. */ + final static String FILE_NAME = "file"; + + /** The position in the lock file used when acquiring a region lock. */ + final static int LOCK_POSITION = 10; + + /** The extent of the lock file locked when acquiring a region lock. */ + final static int LOCK_SIZE = 10; + + /** + * This is the maximum waiting time in seconds for the test to wait for a response from + * 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 = 7; + + @Override + public void tearDown() throws Exception { + stopService(); + super.tearDown(); + } + + /** + * java.nio.channels.FileChannel#tryLock() + * + * Obtains a remote lock, then attempts to acquire a local lock on the same file, + * and checks the behavior. + * checkTryLockBehavior(localLockType, remoteLockType, expectedLocalLockResult) + * expectedLockLockResult: {@code true} if the returned lock should be valid, + * {@code false} otherwise. + */ + public void test_tryLock() throws Exception { + checkTryLockBehavior(LockType.TRY_LOCK, LockType.TRY_LOCK, false /* expectToGetLock */); + checkTryLockBehavior(LockType.TRY_LOCK, LockType.LOCK_ON_REGION_WITH_TRY_LOCK, + false /* expectToGetLock */); + checkTryLockBehavior(LockType.TRY_LOCK, LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, + false /* expectToGetLock */); + checkTryLockBehavior(LockType.TRY_LOCK, LockType.LOCK, false /* expectToGetLock */); + checkTryLockBehavior(LockType.TRY_LOCK, LockType.LOCK_ON_REGION_WITH_LOCK, + false /* expectToGetLock */); + checkTryLockBehavior(LockType.TRY_LOCK, LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, + false /* expectToGetLock */); + } + + /** + * java.nio.channels.FileChannel#tryLock(long, long, boolean) + * + * Obtains a remote lock, then attempts to acquire a local lock on the same file, + * and checks the behavior. + * checkTryLockBehavior(localLockType, remoteLockType, expectedLocalLockResult) + * expectedLockLockResult: {@code true} if the returned lock should be valid, + * {@code false} otherwise. + */ + public void test_tryLockJJZ_Exclusive() throws Exception { + checkTryLockBehavior(LockType.LOCK_ON_REGION_WITH_TRY_LOCK, LockType.TRY_LOCK, + false /* expectToGetLock */); + checkTryLockBehavior(LockType.LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.LOCK_ON_REGION_WITH_TRY_LOCK, false /* expectToGetLock */); + checkTryLockBehavior(LockType.LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK, true /* expectToGetLock */); + checkTryLockBehavior(LockType.LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, false /* expectToGetLock */); + checkTryLockBehavior(LockType.LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK, + true /* expectToGetLock */); + + checkTryLockBehavior(LockType.LOCK_ON_REGION_WITH_TRY_LOCK, LockType.LOCK, + false /* expectToGetLock */); + checkTryLockBehavior(LockType.LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.LOCK_ON_REGION_WITH_LOCK, false /* expectToGetLock */); + checkTryLockBehavior(LockType.LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK, true /* expectToGetLock */); + checkTryLockBehavior(LockType.LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, false /* expectToGetLock */); + checkTryLockBehavior(LockType.LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK , + true /* expectToGetLock */); + } + + /** + * java.nio.channels.FileChannel#tryLock(long, long, boolean) + * + * Obtains a remote lock, then attempts to acquire a local lock on the same file, + * and checks the behavior. + * checkTryLockBehavior(localLockType, remoteLockType, expectedLocalLockResult) + * expectedLockLockResult: {@code true} if the returned lock should be valid, + * {@code false} otherwise. + */ + public void test_tryLockJJZ_Shared() throws Exception { + checkTryLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, LockType.TRY_LOCK, + false /* expectToGetLock */); + checkTryLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.LOCK_ON_REGION_WITH_TRY_LOCK, false /* expectToGetLock */); + checkTryLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK, true /* expectToGetLock */); + checkTryLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, true /* expectToGetLock */); + checkTryLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK, + true /* expectToGetLock */); + + checkTryLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, LockType.LOCK, + false /* expectToGetLock */); + checkTryLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.LOCK_ON_REGION_WITH_LOCK, false /* expectToGetLock */); + checkTryLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK, true /* expectToGetLock */); + checkTryLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, true /* expectToGetLock */); + checkTryLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, + LockType.SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK, + true /* expectToGetLock */); + } + + /** + * java.nio.channels.FileChannel#lock() + * + * Obtains a remote lock, then attempts to acquire a local lock on the same file, + * and checks the behavior. + * checkTryLockBehavior(localLockType, remoteLockType, expectedLocalLockResult) + * expectedLockLockResult: {@code true} if it blocks the local thread, {@code false} otherwise. + */ + public void test_lock() throws Exception { + checkLockBehavior(LockType.LOCK, LockType.LOCK, true /* expectToWait */); + checkLockBehavior(LockType.LOCK, LockType.LOCK_ON_REGION_WITH_LOCK, + true /* expectToWait */); + checkLockBehavior(LockType.LOCK, LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, + true /* expectToWait */); + + checkLockBehavior(LockType.LOCK, LockType.TRY_LOCK, true /* expectToWait */); + checkLockBehavior(LockType.LOCK, LockType.LOCK_ON_REGION_WITH_TRY_LOCK, + true /* expectToWait */); + checkLockBehavior(LockType.LOCK, LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, + true /* expectToWait */); + } + + /** + * java.nio.channels.FileChannel#lock(long, long, boolean) + * + * Obtains a remote lock, then attempts to acquire a local lock on the same file, + * and checks the behavior. + * checkTryLockBehavior(localLockType, remoteLockType, expectedLocalLockResult) + * expectedLockLockResult: {@code true} if blocks the local thread, {@code false} otherwise. + */ + public void test_lockJJZ_Exclusive() throws Exception { + checkLockBehavior(LockType.LOCK_ON_REGION_WITH_LOCK, LockType.LOCK, + true /* expectToWait */); + checkLockBehavior(LockType.LOCK_ON_REGION_WITH_LOCK, LockType.LOCK_ON_REGION_WITH_LOCK, + true /* expectToWait */); + checkLockBehavior(LockType.LOCK_ON_REGION_WITH_LOCK, + LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, true /* expectToWait */); + checkLockBehavior(LockType.LOCK_ON_REGION_WITH_LOCK, + LockType.LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK, false /* expectToWait */); + checkLockBehavior(LockType.LOCK_ON_REGION_WITH_LOCK, + LockType.SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK, false /* expectToWait */); + + checkLockBehavior(LockType.LOCK_ON_REGION_WITH_LOCK, LockType.TRY_LOCK, + true /* expectToWait */); + checkLockBehavior(LockType.LOCK_ON_REGION_WITH_LOCK, LockType.LOCK_ON_REGION_WITH_TRY_LOCK, + true /* expectToWait */); + checkLockBehavior(LockType.LOCK_ON_REGION_WITH_LOCK, + LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, true /* expectToWait */); + checkLockBehavior(LockType.LOCK_ON_REGION_WITH_LOCK, + LockType.LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK, false /* expectToWait */); + checkLockBehavior(LockType.LOCK_ON_REGION_WITH_LOCK, + LockType.SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK, + false /* expectToWait */); + } + + /** + * java.nio.channels.FileChannel#lock(long, long, boolean) + * + * Obtains a remote lock, then attempts to acquire a local lock on the same file, + * and checks the behavior. + * checkTryLockBehavior(localLockType, remoteLockType, expectedLocalLockResult) + * expectedLockLockResult: {@code true} if blocks the local thread, {@code false} otherwise. + */ + public void test_lockJJZ_Shared() throws Exception { + checkLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, LockType.LOCK, + true /* expectToWait */); + checkLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, + LockType.LOCK_ON_REGION_WITH_LOCK, true /* expectToWait */); + checkLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, + LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, false /* expectToWait */); + checkLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, + LockType.LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK, false /* expectToWait */); + checkLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, + LockType.SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK, false /* expectToWait */); + + checkLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, LockType.TRY_LOCK, + true /* expectToWait */); + checkLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, + LockType.LOCK_ON_REGION_WITH_TRY_LOCK, true /* expectToWait */); + checkLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, + LockType.SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, false /* expectToWait */); + checkLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, + LockType.LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK, false /* expectToWait */); + checkLockBehavior(LockType.SHARED_LOCK_ON_REGION_WITH_LOCK, + LockType.SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK, + false /* expectToWait */); + } + + /** + * Checks the behavior of java.nio.Channels.FileChannel#tryLock() and #tryLock(J, J, Z) + * + * @param localLockType the type of lock to be acquired by the test + * @param remoteLockType the type of lock to be acquired by the remote service + * @param expectToGetLock {@code true}, if the lock should be acquired even when the + * service holds a {@code remoteLockType} lock, false otherwise. + */ + private void checkTryLockBehavior(LockType localLockType, LockType remoteLockType, + boolean expectToGetLock) throws Exception { + IntentReceiver.resetReceiverState(); + + // Request that the remote lock be obtained. + getContext().startService(new Intent(getContext(), LockHoldingService.class) + .putExtra(LockHoldingService.LOCK_TYPE_KEY, remoteLockType)); + + // Wait for a signal that the remote lock is definitely held. + assertTrue(IntentReceiver.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. + if (expectToGetLock) { + FileLock fileLock = acquire(localLockType); + assertNotNull(fileLock); + assertTrue(fileLock.isValid()); + } else { + assertNull(acquire(localLockType)); + } + // Release the remote lock. + stopService(); + } + + /** + * Checks the java.nio.channels.FileChannel.lock()/lock(J, J, Z) behavior. + * + * @param localLockType type of lock to be acquired by the test + * @param remoteLockType type of lock to be acquired by the remote service. + * @param expectToWait {@code true}, if the local thread must wait for the remote + * service to release the lock, {@code false} otherwise. + */ + private void checkLockBehavior(LockType localLockType, LockType remoteLockType, + boolean expectToWait) throws Exception { + IntentReceiver.resetReceiverState(); + + // The amount of time the remote service should hold lock. + 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 = 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.LOCK_BEHAVIOR_RELEASE_AND_NOTIFY_KEY, true); + + getContext().startService(sendIntent); + + // Wait for the service to hold the lock and notify for the same. + assertTrue(IntentReceiver.lockHeldLatch.await(MAX_WAIT_TIME, SECONDS)); + + long localLockNotObtainedTime = System.currentTimeMillis(); + + // Acquire the lock locally. + FileLock fileLock = acquire(localLockType); + long localLockObtainedTime = System.currentTimeMillis(); + + // Wait until the remote lock has definitely been released. + assertTrue(IntentReceiver.lockReleasedLatch.await(MAX_WAIT_TIME, SECONDS)); + + Bundle remoteLockReleasedBundle = IntentReceiver.lockReleasedBundle; + long remoteLockNotReleasedTime = + remoteLockReleasedBundle.getLong(LockHoldingService.LOCK_NOT_YET_RELEASED_TIMESTAMP); + long remoteLockReleasedTime = + remoteLockReleasedBundle.getLong(LockHoldingService.LOCK_DEFINITELY_RELEASED_TIMESTAMP); + + // We want the test to be notified well before the service releases the lock, so that + // we can be sure that it tried obtaining the lock before the service actually released it. + // Therefore, a two seconds time interval provides the test to get prepare and try to obtain + // the lock. If this fails, it doesn't mean they definitely didn't overlap + // but we can't be sure and the test may not be valid. This is why we hold the lock + // remotely for a long time compared to the delays we expect for intents to propagate + // between processes. + assertTrue(remoteLockNotReleasedTime - localLockNotObtainedTime > + sufficientOverlappingTimeInMillis); + + if (expectToWait) { + + // The remoteLockReleaseTime is captured after the lock was released by the + // service. The localLockObtainedTime is captured after the lock was obtained by this + // thread. Therefore, there is a degree of slop inherent in the two times. We assert + // that they are "close" to each other, but we cannot assert any ordering. + assertTrue(Math.abs(localLockObtainedTime - remoteLockReleasedTime) < + lockReleasedAndReacquiredTimeDeltaInMillis); + } else { + // The remoteLockNotReleaseTime is captured before the lock was released by the + // service. The localLockObtainedTime is captured after the lock was obtained by this + // thread. The local thread should be able to get the lock before the remote thread + // definitely release it. If this test fails it may not indicate a problem, but it + // indicates we cannot be sure the test was successful the local lock attempt and the + // remote lock attempt did not overlap. + assertTrue(localLockObtainedTime < remoteLockNotReleasedTime); + } + + // Asserting if the fileLock is valid. + assertTrue(fileLock.isValid()); + stopService(); + } + + /** + * Requests and waits for the service to stop + */ + void stopService() throws Exception { + getContext().stopService(new Intent(getContext(), LockHoldingService.class)); + assertTrue(IntentReceiver.onStopLatch.await(MAX_WAIT_TIME, SECONDS)); + deleteDir(); + } + + enum LockType { + + /** Equivalent to {@code tryLock()} */ + TRY_LOCK, + + /** Equivalent to {@code tryLock({@link #LOCK_POSITION}, {@link #LOCK_SIZE}, false)} */ + LOCK_ON_REGION_WITH_TRY_LOCK, + + /** + * Equivalent to {@code tryLock({@code {@link #LOCK_POSITION} + {@link #LOCK_SIZE}}, + * {@link #LOCK_SIZE}, false)} + */ + LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK, + + /** Equivalent to {@code tryLock({@link #LOCK_POSITION}, {@link #LOCK_SIZE}, true)} */ + SHARED_LOCK_ON_REGION_WITH_TRY_LOCK, + + /** + * Equivalent to {@code tryLock({@code {@link #LOCK_POSITION} + {@link #LOCK_SIZE}}, + * {@link #LOCK_SIZE}, true)} + */ + SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK, + + /** Equivalent to {@code lock()} */ + LOCK, + + /** Equivalent to {code lock({@link #LOCK_POSITION}, {@link #LOCK_SIZE}, false)} */ + LOCK_ON_REGION_WITH_LOCK, + + /** + * Equivalent to {@code lock({@code {@link #LOCK_POSITION} + {@link #LOCK_SIZE}}, + * {@link #LOCK_SIZE}, false)} + */ + LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK, + + /** Equivalent to {@code lock({@link #LOCK_POSITION}, {@link #LOCK_SIZE}, true)} */ + SHARED_LOCK_ON_REGION_WITH_LOCK, + + /** + * Equivalent to {@code lock({@code {@link #LOCK_POSITION} + {@link #LOCK_SIZE}}, + * {@link #LOCK_SIZE}, true)} + */ + SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK, + } + + /** + * Tries to acquire a lock of {@code lockType} on the file returned by + * {@link #createFileInDir()} method. + * + * @param lockType a {@link LockType} enum. + * Permitted lock types: + * {@link LockType#TRY_LOCK} + * {@link LockType#LOCK_ON_REGION_WITH_TRY_LOCK} + * {@link LockType#LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK} + * {@link LockType#SHARED_LOCK_ON_REGION_WITH_TRY_LOCK} + * {@link LockType#SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK} + * {@link LockType#LOCK} + * {@link LockType#LOCK_ON_REGION_WITH_LOCK} + * {@link LockType#LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK} + * {@link LockType#SHARED_LOCK_ON_REGION_WITH_LOCK} + * {@link LockType#SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK} + * @return Returns the lock returned by the lock method. + * @throws UnsupportedOperationException + * If the {@code lockType} is of non recognized type. + */ + static FileLock acquire(LockType lockType) throws IOException { + File file = createFileInDir(); + file.createNewFile(); + switch (lockType) { + case TRY_LOCK: + return new FileOutputStream(file).getChannel().tryLock(); + case LOCK_ON_REGION_WITH_TRY_LOCK: + return new FileOutputStream(file).getChannel() + .tryLock(LOCK_POSITION, LOCK_SIZE, false /*isShared*/); + case LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK: + return new FileOutputStream(file).getChannel() + .tryLock(LOCK_POSITION + LOCK_SIZE, LOCK_SIZE, false /*isShared*/); + case SHARED_LOCK_ON_REGION_WITH_TRY_LOCK: + return new FileInputStream(file).getChannel() + .tryLock(LOCK_POSITION, LOCK_SIZE, true /*isShared*/); + case SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_TRY_LOCK: + return new FileInputStream(file).getChannel() + .tryLock(LOCK_POSITION + LOCK_SIZE, LOCK_SIZE, true /*isShared*/); + case LOCK: + return new FileOutputStream(file).getChannel().lock(); + case LOCK_ON_REGION_WITH_LOCK: + return new FileOutputStream(file).getChannel() + .lock(LOCK_POSITION, LOCK_SIZE, false /*isShared*/); + case LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK: + return new FileOutputStream(file).getChannel() + .lock(LOCK_POSITION + LOCK_SIZE, LOCK_SIZE, false /*isShared*/); + case SHARED_LOCK_ON_REGION_WITH_LOCK: + return new FileInputStream(file).getChannel() + .lock(LOCK_POSITION, LOCK_SIZE, true /*isShared*/); + case SHARED_LOCK_ON_NON_OVERLAPPING_REGION_WITH_LOCK: + return new FileInputStream(file).getChannel() + .lock(LOCK_POSITION + LOCK_SIZE, LOCK_SIZE, true /*isShared*/); + default: + throw new UnsupportedOperationException("Unknown lock type"); + } + } + + /** + * Creates a file named {@link #FILE_NAME} inside a directory named {@link #DIR_NAME} on + * the external storage directory. + */ + static File createFileInDir() throws IOException { + File dir = new File(Environment.getExternalStorageDirectory(), DIR_NAME); + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + throw new IOException("External storage is not mounted"); + } else if (!dir.mkdirs() && !dir.isDirectory()) { + throw new IOException("Cannot create directory for device info files"); + } else { + return new File(dir, FILE_NAME); + } + } + + /** + * Deletes the folder {@link #DIR_NAME} on the external storage directory along with all the + * files inside it. + */ + static void deleteDir() { + File dir = new File(Environment.getExternalStorageDirectory(), DIR_NAME); + if (dir.isDirectory()) { + String[] children = dir.list(); + for (String child : children) { + new File(dir, child).delete(); + } + dir.delete(); + } + } + + /** + * 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. + */ + public static class IntentReceiver extends BroadcastReceiver { + + static CountDownLatch onStartLatch; + + static CountDownLatch onStopLatch; + + static CountDownLatch lockHeldLatch; + + static volatile Bundle lockHeldBundle; + + static CountDownLatch lockReleasedLatch; + + static volatile Bundle lockReleasedBundle; + + /** + * Reset the IntentReceiver 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); + lockHeldLatch = new CountDownLatch(1); + lockReleasedLatch = new CountDownLatch(1); + lockHeldBundle = null; + lockReleasedBundle = null; + } + + @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; + } + } + } +} + diff --git a/tests/tests/libcorefileio/src/android/cts/FileChannelTryLockTest.java b/tests/tests/libcorefileio/src/android/cts/FileChannelTryLockTest.java deleted file mode 100644 index 61a59ca6f46..00000000000 --- a/tests/tests/libcorefileio/src/android/cts/FileChannelTryLockTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2016 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.cts; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.Environment; -import android.test.AndroidTestCase; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.channels.FileLock; -import java.nio.channels.OverlappingFileLockException; - -@SuppressWarnings("deprecation") -public class FileChannelTryLockTest extends AndroidTestCase { - - final static String dirName = "CtsFIleIOTest"; - - final static String sharedFileName = "sharedFile"; - - public void testFileLockWithMultipleProcess() throws InterruptedException, IOException { - IntentReceiver receiver = new IntentReceiver(); - getContext().startService(new Intent(getContext(), LockHoldingService.class)); - synchronized (receiver.notifier) { - receiver.notifier.wait(10000); - } - File sharedFile = createFileInDir(dirName, sharedFileName); - assertNull(new FileOutputStream(sharedFile).getChannel().tryLock()); - getContext().stopService(new Intent(getContext(), LockHoldingService.class)); - } - - public void testFileLockWithSingleProcess() throws IOException { - File file = createFileInDir(dirName, "sharedFileForSingleProcess"); - FileLock fileLock1 = new FileOutputStream(file).getChannel().tryLock(); - try { - new FileOutputStream(file).getChannel().tryLock(); - fail(); - } catch (OverlappingFileLockException expected) { - } - } - - static File createFileInDir(String dirName, String fileName) throws IOException { - File dir = new File(Environment.getExternalStorageDirectory(), dirName); - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - throw new IOException("External storage is not mounted"); - } else if (!dir.mkdirs() && !dir.isDirectory()) { - throw new IOException("Cannot create directory for device info files"); - } else { - return new File(dir, fileName); - } - } - - static void deleteDir() { - File dir = new File(Environment.getExternalStorageDirectory(), dirName); - if (dir.isDirectory()) { - String[] children = dir.list(); - for (int i = 0; i < children.length; i++) { - new File(dir, children[i]).delete(); - } - dir.delete(); - } - } - - @Override - public void tearDown() throws Exception { - getContext().stopService(new Intent(getContext(), LockHoldingService.class)); - deleteDir(); - } - - public static class IntentReceiver extends BroadcastReceiver { - - public final static Object notifier = new Object(); - - @Override - public void onReceive(Context context, Intent intent) { - synchronized (notifier) { - notifier.notify(); - } - } - } -} - diff --git a/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java b/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java index be6a3526125..6ac73b0df05 100644 --- a/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java +++ b/tests/tests/libcorefileio/src/android/cts/LockHoldingService.java @@ -21,18 +21,74 @@ import android.content.Intent; import android.os.IBinder; import android.util.Log; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileLock; +/** + * 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 + * multiple processes. + */ public class LockHoldingService extends Service { - final String TAG = "CtsLibcoreFileIOTestCases"; + /** + * The key of the Bundle extra used to record a time after a lock is released by the service. + */ + static final String LOCK_DEFINITELY_RELEASED_TIMESTAMP = "lockReleasedTimestamp"; + + /** + * The key of the Bundle extra used to record just before the lock is released by the service. + */ + static final String LOCK_NOT_YET_RELEASED_TIMESTAMP = "lockNotReleasedTimestamp"; + + /** + * The key of the Bundle extra used to send general notifications to the test. + */ + 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"; + + /** + * The value for the notification sent to the test after the lock is released + */ + static final String NOTIFICATION_LOCK_RELEASED = "lockReleased"; - File file = null; + /** + * The key of the Bundle extra used to send time for which the service should wait before + * releasing the lock. + */ + static final String TIME_TO_HOLD_LOCK_KEY = "timeToHoldLock"; - FileLock fileLock = null; + /** + * The key of the Bundle extra used for the type of lock to be held. + */ + static final String LOCK_TYPE_KEY = "lockType"; + + /** + * The key of the Bundle extra used to let he service know whether to release the lock after + * some time. + */ + static final String LOCK_BEHAVIOR_RELEASE_AND_NOTIFY_KEY = "releaseAndNotify"; + + static final String ACTION_TYPE_FOR_INTENT_COMMUNICATION + = "android.cts.CtsLibcoreFileIOTestCases"; + + final String LOG_MESSAGE_TAG = "CtsLibcoreFileIOTestCases"; + + private FileLock fileLock = null; public IBinder onBind(Intent intent) { return null; @@ -41,27 +97,81 @@ public class LockHoldingService extends Service { @Override public int onStartCommand(Intent intent, int flags, int startID) { try { - this.file = FileChannelTryLockTest.createFileInDir(FileChannelTryLockTest.dirName, - FileChannelTryLockTest.sharedFileName); - try { - this.fileLock = new FileOutputStream(file).getChannel().tryLock(); - } catch (IOException e) { - Log.e(TAG, e.getMessage()); + if (intent.getBooleanExtra(LOCK_BEHAVIOR_RELEASE_AND_NOTIFY_KEY, false)) { + acquireLockAndThenWaitThenRelease(intent); + } else { + acquireLock(intent); } - } catch (IOException e) { - Log.e(TAG, e.getMessage()); + } catch (Exception e) { + Log.e(LOG_MESSAGE_TAG, e.getMessage()); } - sendBroadcast(new Intent().setAction("android.cts.CtsLibcoreFileIOTestCases")); return START_STICKY; } + /** + * Acquires the lock asked by the test indefinitely. + */ + private void acquireLock(Intent intent) throws IOException { + FileChannelInterProcessLockTest.LockType lockType = + (FileChannelInterProcessLockTest.LockType)intent.getSerializableExtra( + LOCK_TYPE_KEY); + + // Acquire the lock based on the information contained in the intent received. + this.fileLock = FileChannelInterProcessLockTest.acquire(lockType); + Intent responseIntent = new Intent().putExtra(NOTIFICATION_KEY, NOTIFICATION_LOCK_HELD) + .setAction(ACTION_TYPE_FOR_INTENT_COMMUNICATION); + sendBroadcast(responseIntent); + } + + /** + * Acquires and holds the lock for a time specified by the test. Sends a broadcast message after + * releasing the lock. + */ + private void acquireLockAndThenWaitThenRelease(Intent intent) + throws IOException, InterruptedException { + long lockHoldTimeMillis = intent.getLongExtra(TIME_TO_HOLD_LOCK_KEY, 0); + + // Acquire the lock. + FileChannelInterProcessLockTest.LockType lockType = + (FileChannelInterProcessLockTest.LockType)intent.getSerializableExtra( + LOCK_TYPE_KEY); + this.fileLock = FileChannelInterProcessLockTest.acquire(lockType); + + // Signal the lock is now held. + Intent heldIntent = new Intent() + .putExtra(NOTIFICATION_KEY, NOTIFICATION_LOCK_HELD) + .setAction(ACTION_TYPE_FOR_INTENT_COMMUNICATION); + sendBroadcast(heldIntent); + + Thread.sleep(lockHoldTimeMillis); + + long lockNotReleasedTimestamp = System.currentTimeMillis(); + + // Release the lock + fileLock.release(); + + long lockReleasedTimestamp = System.currentTimeMillis(); + + // Signal the lock is released and some information about timing. + Intent releaseIntent = new Intent() + .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); + } + @Override public void onDestroy() { try { - fileLock.close(); + if (fileLock != null) { + fileLock.release(); + } } catch (IOException e) { - Log.e(TAG, e.getMessage()); + Log.e(LOG_MESSAGE_TAG, e.getMessage()); } - FileChannelTryLockTest.deleteDir(); + Intent intent = new Intent().putExtra(NOTIFICATION_KEY, NOTIFICATION_STOP) + .setAction(ACTION_TYPE_FOR_INTENT_COMMUNICATION); + sendBroadcast(intent); } } |