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;
     }
 }