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 
Latch(Entry entry)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          */
await()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          */
getRequestCode()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 
Entry(int requestCode)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      */
bindThread()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      */
onActivityResult(int requestCode, int resultCode)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
getRequestCode()117     private int getRequestCode() {
118         return Thread.currentThread().hashCode();
119     }
120 }
121