1 /*
2  * Copyright (C) 2023 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 com.android.bedstead.nene.utils;
18 
19 import android.app.IntentService;
20 import android.content.Intent;
21 import android.util.Log;
22 
23 import com.android.bedstead.nene.TestApis;
24 
25 import java.util.Map;
26 import java.util.Random;
27 import java.util.concurrent.ConcurrentHashMap;
28 import java.util.concurrent.CountDownLatch;
29 import java.util.concurrent.TimeUnit;
30 
31 /**
32  * Service used by {@link BlockingIntentSender}.
33  *
34  * <p>See {@link BlockingIntentSender} for usage.
35  */
36 public final class BlockingIntentSenderService extends IntentService {
37 
38     private static final String LOG_TAG = "BlockingIntentSenderService";
39 
40     private static final String BLOCKING_INTENT_SENDER_ID_KEY = "BlockingIntentSenderId";
41 
42     private static final long DEFAULT_TIMEOUT = 30;
43     private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;
44 
45     private static final Map<String, CountDownLatch> sLatches = new ConcurrentHashMap<>();
46     private static final Map<String, Intent> sReceivedIntents = new ConcurrentHashMap<>();
47 
48     // TODO(b/273197248): This only allows us to have one intentsender at a time - this is because
49     // for some reason it appeared to be losing the extra value on receiving the intent -
50     // to reproduce just comment out the FIXED_ID stuff and run
51     // com.android.bedstead.nene.packages.PackagesTest#install_instrumentedUser_isInstalled
52     private static final String FIXED_ID = "FIXED";
53 
54     /** Only for use by {@link BlockingIntentSender}. */
BlockingIntentSenderService()55     public BlockingIntentSenderService() {
56         super("BlockingIntentSenderService");
57     }
58 
59     /** Only for use by {@link BlockingIntentSender}. */
register()60     public static Intent register() {
61         String id = Long.toString(new Random().nextLong());
62         id = FIXED_ID;
63 
64         Intent intent = new Intent();
65         intent.setClass(
66                 TestApis.context().instrumentedContext(), BlockingIntentSenderService.class);
67         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
68         intent.putExtra(BLOCKING_INTENT_SENDER_ID_KEY, id);
69 
70         sLatches.put(id, new CountDownLatch(1));
71 
72         return intent;
73     }
74 
getId(Intent intent)75     private static String getId(Intent intent) {
76 //        return intent.getStringExtra(BLOCKING_INTENT_SENDER_ID_KEY);
77         return FIXED_ID;
78     }
79 
80     /** Only for use by {@link BlockingIntentSender}. */
unregister(Intent intent)81     public static void unregister(Intent intent) {
82         String id = getId(intent);
83         sLatches.remove(id);
84         sReceivedIntents.remove(id);
85     }
86 
87     /** Only for use by {@link BlockingIntentSender}. */
await(Intent intent)88     public static Intent await(Intent intent) {
89         return await(intent, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT);
90     }
91 
92     /** Only for use by {@link BlockingIntentSender}. */
await(Intent intent, long timeout, TimeUnit unit)93     public static Intent await(Intent intent, long timeout, TimeUnit unit) {
94         String id = getId(intent);
95 
96         CountDownLatch latch = sLatches.get(id);
97         if (latch == null) {
98             throw new IllegalStateException(
99                     "Awaiting but no latch registered for intent " + intent);
100         }
101 
102         try {
103             latch.await(timeout, unit);
104         } catch (InterruptedException e) {
105             throw new RuntimeException(e);
106         }
107 
108         return sReceivedIntents.get(id);
109     }
110 
111     @Override
onHandleIntent(Intent intent)112     protected void onHandleIntent(Intent intent) {
113         String id = getId(intent);
114 
115         CountDownLatch latch = sLatches.get(id);
116         if (latch == null) {
117             Log.e(LOG_TAG,
118                     "Received start intent at BlockingIntentSenderService but no "
119                             + "latch registered for id " + id + " intent " + intent);
120             return;
121         }
122 
123         sReceivedIntents.put(id, intent);
124         latch.countDown();
125     }
126 }
127