Home | History | Annotate | Download | only in helpers
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package android.hardware.cts.helpers;
     18 
     19 import java.util.HashMap;
     20 import java.util.concurrent.CountDownLatch;
     21 
     22 /**
     23  * An abstraction on top of {@link CountDownLatch} to synchronize the results of Activities
     24  * started by a parent activity.
     25  *
     26  * It holds a {@link CountDownLatch} latch for each thread that requests synchronization.
     27  *
     28  * Each thread requests a {@link Latch} to synchronize an Activity that will be started, by invoking
     29  * {@link #bindThread()}, this guarantees that a latch is associated with the thread, and the result
     30  * can be retrieved.
     31  */
     32 public class ActivityResultMultiplexedLatch {
     33     private static final String TAG = "ActivityResultMultiplexedLatch";
     34 
     35     private final HashMap<Integer, Entry> mActivityEntries = new HashMap<Integer, Entry>();
     36 
     37     /**
     38      * A latch for a bound thread.
     39      * Applications get an instance by invoking {@link ActivityResultMultiplexedLatch#bindThread()}.
     40      */
     41     public class Latch {
     42         private Entry mEntry;
     43 
     44         private Latch(Entry entry) {
     45             mEntry = entry;
     46         }
     47 
     48         /**
     49          * Awaits for the Activity bound to unblock the current thread.
     50          *
     51          * @return The result code of the Activity executed.
     52          */
     53         public int await() throws InterruptedException {
     54             mEntry.latch.await();
     55             return mEntry.resultCode;
     56         }
     57 
     58         /**
     59          * @return A request code for the bound thread. It can be passed to the Activity to start.
     60          */
     61         public int getRequestCode() {
     62             return mEntry.requestCode;
     63         }
     64     }
     65 
     66     /**
     67      * A class that represents the state for each thread/Activity being tracked.
     68      */
     69     private class Entry {
     70         public final CountDownLatch latch = new CountDownLatch(1);
     71         public final int requestCode;
     72 
     73         public volatile int resultCode;
     74 
     75         public Entry(int requestCode) {
     76             this.requestCode = requestCode;
     77         }
     78     }
     79 
     80     /**
     81      * Binds a thread with this object.
     82      *
     83      * @return A request code (or session Id) for the bound thread.
     84      */
     85     public Latch bindThread() {
     86         Entry entry;
     87         int requestCode = getRequestCode();
     88 
     89         synchronized (mActivityEntries) {
     90             if (mActivityEntries.containsKey(requestCode)) {
     91                 throw new IllegalStateException("The thread has already been bound.");
     92             }
     93             entry = new Entry(requestCode);
     94             mActivityEntries.put(requestCode, entry);
     95         }
     96 
     97         return new Latch(entry);
     98     }
     99 
    100     /**
    101      * Used by the owner of the instance to record an Activity's result.
    102      */
    103     public void onActivityResult(int requestCode, int resultCode) {
    104         Entry entry;
    105         synchronized (mActivityEntries) {
    106             entry = mActivityEntries.remove(requestCode);
    107         }
    108         if (entry == null) {
    109             return;
    110         }
    111 
    112         entry.resultCode = resultCode;
    113         entry.latch.countDown();
    114     }
    115 
    116     // there is no need for a better request Id, only one Activity can be launched at any time
    117     private int getRequestCode() {
    118         return Thread.currentThread().hashCode();
    119     }
    120 }
    121