1 /*
2  * Copyright 2024 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  *      https://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 package com.android.devicediagnostics.evaluated
17 
18 import android.app.Activity
19 import android.content.Intent
20 import android.os.Parcel
21 import android.os.Parcelable
22 import com.android.devicediagnostics.DiagnosticsActivity
23 import com.android.devicediagnostics.Protos.TestResults
24 import com.android.devicediagnostics.Protos.TrustedDeviceInfo
25 
26 // When running through a trusted/evaluated test sequence, this key is used to communicate
27 // test state between activities.
28 private const val TEST_STATE_KEY = "test_state"
29 
30 // Helper class for currying data between activities. This is only constructed when running
31 // multiple tests in succession.
32 class TestState(
33     var testResults: TestResults,
34     var trustedDevice: TrustedDeviceInfo,
35 ) : Parcelable {
36     constructor(
37         parcel: Parcel
38     ) : this(
39         testResults = TestResults.parseFrom(parcel.createByteArray()),
40         trustedDevice = TrustedDeviceInfo.parseFrom(parcel.createByteArray())
41     ) {}
42 
writeToParcelnull43     override fun writeToParcel(dest: Parcel, flags: Int) {
44         dest.writeByteArray(testResults.toByteArray())
45         dest.writeByteArray(trustedDevice.toByteArray())
46     }
47 
describeContentsnull48     override fun describeContents(): Int {
49         return 0
50     }
51 
52     companion object CREATOR : Parcelable.Creator<TestState> {
createFromParcelnull53         override fun createFromParcel(parcel: Parcel): TestState {
54             return TestState(parcel)
55         }
56 
newArraynull57         override fun newArray(size: Int): Array<TestState?> {
58             return arrayOfNulls(size)
59         }
60 
fromActivitynull61         fun fromActivity(activity: Activity): TestState? {
62             return activity.intent.getParcelableExtra(TEST_STATE_KEY, TestState::class.java)
63         }
64     }
65 }
66 
startFullTestFlownull67 fun startFullTestFlow(from: Activity, trustedDeviceInfo: TrustedDeviceInfo) {
68     nextTestActivity(from, ScreenTestIntroActivity::class.java.name) {
69         val results = TestResults.getDefaultInstance()
70         val state = TestState(results, trustedDeviceInfo)
71         it.putExtra(TEST_STATE_KEY, state)
72     }
73 }
74 
75 // Advance through test subactivities (eg TouchTestIntro -> TouchTest).
nextTestActivitynull76 fun nextTestActivity(
77     from: Activity,
78     nextClass: String,
79     withIntent: ((intent: Intent) -> Unit)? = null
80 ) {
81     val intent = Intent()
82     intent.setClassName(from, nextClass)
83 
84     val state = TestState.fromActivity(from)
85     if (state != null) intent.putExtra(TEST_STATE_KEY, state)
86     if (withIntent != null) withIntent(intent)
87 
88     from.startActivity(intent)
89 }
90 
91 // Advance to the next activity group in the test flow, concluding the current test.
nextTestnull92 fun nextTest(from: Activity, withReport: (builder: TestResults.Builder) -> Unit) {
93     if (isOneShotTest(from)) {
94         Intent(from, DiagnosticsActivity::class.java).also {
95             it.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
96             from.startActivity(it)
97             from.finish()
98         }
99         return
100     }
101 
102     val state = TestState.fromActivity(from)!!
103     val builder = state.testResults.toBuilder()
104     if (withReport != null) withReport(builder)
105     state.testResults = builder.build()
106 
107     val nextActivityClass =
108         when (from.javaClass.name) {
109             ScreenTestFinalizeActivity::class.java.name -> TouchTestIntroActivity::class.java.name
110             TouchTestFinalizeActivity::class.java.name ->
111                 EvaluationFinalizeActivity::class.java.name
112             else -> null
113         }
114 
115     val intent = Intent()
116     intent.setClassName(from, nextActivityClass!!)
117     intent.putExtra(TEST_STATE_KEY, state)
118 
119     // Don't allow navigating back once we're submitting the results.
120     if (nextActivityClass == EvaluationFinalizeActivity::class.java.name)
121         intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK.or(Intent.FLAG_ACTIVITY_CLEAR_TASK)
122 
123     from.startActivity(intent)
124 }
125 
126 // Returns whether the current activity is part of one-shot test flow.
isOneShotTestnull127 fun isOneShotTest(from: Activity): Boolean {
128     val extras = from.intent.extras
129     if (extras == null) return true
130     return !extras.containsKey(TEST_STATE_KEY)
131 }
132