blob: 05894d5f1c8bb0d98eeefbfcdfa28d83c97c6011 [file] [log] [blame]
/*
* 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;
}
}
}
}