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 android.adpf.atom.app;
18 
19 import static android.adpf.atom.common.ADPFAtomTestConstants.ACTION_CREATE_DEAD_TIDS_THEN_GO_BACKGROUND;
20 import static android.adpf.atom.common.ADPFAtomTestConstants.ACTION_CREATE_REGULAR_HINT_SESSIONS;
21 import static android.adpf.atom.common.ADPFAtomTestConstants.CONTENT_KEY_RESULT_TIDS;
22 import static android.adpf.atom.common.ADPFAtomTestConstants.CONTENT_KEY_UID;
23 import static android.adpf.atom.common.ADPFAtomTestConstants.CONTENT_URI_STRING;
24 import static android.adpf.atom.common.ADPFAtomTestConstants.INTENT_ACTION_KEY;
25 
26 import static org.junit.Assert.assertNotNull;
27 import static org.junit.Assume.assumeNotNull;
28 
29 import android.app.Activity;
30 import android.content.ContentValues;
31 import android.content.Intent;
32 import android.net.Uri;
33 import android.os.Build;
34 import android.os.Bundle;
35 import android.os.PerformanceHintManager;
36 import android.os.Process;
37 import android.util.ArrayMap;
38 import android.util.Log;
39 
40 import com.android.compatibility.common.util.PropertyUtil;
41 
42 import java.util.Map;
43 import java.util.StringJoiner;
44 import java.util.concurrent.CountDownLatch;
45 import java.util.concurrent.atomic.AtomicInteger;
46 
47 /** An activity which performs ADPF actions. */
48 public class ADPFAtomTestActivity extends Activity {
49     private static final String TAG = ADPFAtomTestActivity.class.getSimpleName();
50 
51 
52     private final Map<String, Bundle> mResult = new ArrayMap<>();
53 
54     private static final int FIRST_API_LEVEL = PropertyUtil.getFirstApiLevel();
55 
56     @Override
onCreate(Bundle bundle)57     public void onCreate(Bundle bundle) {
58         super.onCreate(bundle);
59 
60         final Intent intent = this.getIntent();
61         assertNotNull(intent);
62         final String action = intent.getStringExtra(INTENT_ACTION_KEY);
63         assertNotNull(action);
64         switch (action) {
65             case ACTION_CREATE_DEAD_TIDS_THEN_GO_BACKGROUND:
66                 try {
67                     final int[] tids = createHintSessionWithExitedThreads();
68                     final StringJoiner sb = new StringJoiner(",");
69                     for (int tid : tids) {
70                         sb.add(String.valueOf(tid));
71                     }
72                     ContentValues values = new ContentValues();
73                     values.put(CONTENT_KEY_RESULT_TIDS, sb.toString());
74                     values.put(CONTENT_KEY_UID, String.valueOf(Process.myUid()));
75                     assertNotNull(
76                             getContentResolver().insert(Uri.parse(CONTENT_URI_STRING), values));
77                 } catch (InterruptedException e) {
78                     throw new RuntimeException(e);
79                 }
80                 moveTaskToBack(true);
81                 Log.i(TAG, "Moved task ADPFHintSessionDeviceActivity to back");
82                 break;
83             case ACTION_CREATE_REGULAR_HINT_SESSIONS:
84                 PerformanceHintManager.Session session = createPerformanceHintSession();
85                 if (FIRST_API_LEVEL < Build.VERSION_CODES.S) {
86                     assumeNotNull(session);
87                 } else {
88                     assertNotNull(session);
89                 }
90                 break;
91         }
92     }
93 
94     /**
95      * Read the run result of a specific action
96      */
getRunResult(String actionKey)97     public Bundle getRunResult(String actionKey) {
98         synchronized (mResult) {
99             return mResult.get(actionKey);
100         }
101     }
102 
createHintSessionWithExitedThreads()103     private int[] createHintSessionWithExitedThreads() throws InterruptedException {
104         PerformanceHintManager hintManager = getApplicationContext().getSystemService(
105                 PerformanceHintManager.class);
106         assertNotNull(hintManager);
107         CountDownLatch stopLatch = new CountDownLatch(1);
108         int[] tids = createThreads(5, stopLatch);
109         hintManager.createHintSession(tids, 100);
110         stopLatch.countDown();
111         return tids;
112     }
113 
createThreads(int tidCnt, CountDownLatch stopLatch)114     private int[] createThreads(int tidCnt, CountDownLatch stopLatch) throws InterruptedException {
115         int[] tids = new int[tidCnt];
116         CountDownLatch latch = new CountDownLatch(tidCnt);
117         AtomicInteger k = new AtomicInteger(0);
118         for (int i = 0; i < tidCnt; i++) {
119             final Thread t = new Thread(() -> {
120                 tids[k.getAndIncrement()] = android.os.Process.myTid();
121                 try {
122                     latch.countDown();
123                     stopLatch.await();
124                 } catch (InterruptedException e) {
125                     throw new RuntimeException(e);
126                 }
127             });
128             t.start();
129         }
130         latch.await();
131         return tids;
132     }
133 
createPerformanceHintSession()134     private PerformanceHintManager.Session createPerformanceHintSession() {
135         final long testTargetDuration = 12345678L;
136         PerformanceHintManager hintManager = getApplicationContext().getSystemService(
137                 PerformanceHintManager.class);
138         assertNotNull(hintManager);
139         return hintManager.createHintSession(
140                 new int[]{android.os.Process.myTid()}, testTargetDuration);
141     }
142 }
143