1 /*
2  * Copyright (C) 2017 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.autofillservice.cts;
18 
19 import static com.google.common.truth.Truth.assertWithMessage;
20 
21 import android.app.Activity;
22 import android.app.PendingIntent;
23 import android.app.assist.AssistStructure;
24 import android.autofillservice.cts.CannedFillResponse.CannedDataset;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentSender;
28 import android.os.Bundle;
29 import android.os.Parcelable;
30 import android.util.Log;
31 import android.util.SparseArray;
32 import android.view.autofill.AutofillManager;
33 
34 import com.google.common.base.Preconditions;
35 
36 import java.util.ArrayList;
37 
38 /**
39  * This class simulates authentication at the dataset at reponse level
40  */
41 public class AuthenticationActivity extends AbstractAutoFillActivity {
42 
43     private static final String TAG = "AuthenticationActivity";
44     private static final String EXTRA_DATASET_ID = "dataset_id";
45     private static final String EXTRA_RESPONSE_ID = "response_id";
46 
47     private static Bundle sData;
48     private static final SparseArray<CannedDataset> sDatasets = new SparseArray<>();
49     private static final SparseArray<CannedFillResponse> sResponses = new SparseArray<>();
50     private static final ArrayList<PendingIntent> sPendingIntents = new ArrayList<>();
51 
52     private static Object sLock = new Object();
53 
54     // Guarded by sLock
55     private static int sResultCode;
56 
resetStaticState()57     static void resetStaticState() {
58         setResultCode(RESULT_OK);
59         sDatasets.clear();
60         sResponses.clear();
61         for (int i = 0; i < sPendingIntents.size(); i++) {
62             final PendingIntent pendingIntent = sPendingIntents.get(i);
63             Log.d(TAG, "Cancelling " + pendingIntent);
64             pendingIntent.cancel();
65         }
66     }
67 
68     /**
69      * Creates an {@link IntentSender} with the given unique id for the given dataset.
70      */
createSender(Context context, int id, CannedDataset dataset)71     public static IntentSender createSender(Context context, int id,
72             CannedDataset dataset) {
73         Preconditions.checkArgument(id > 0, "id must be positive");
74         Preconditions.checkState(sDatasets.get(id) == null, "already have id");
75         sDatasets.put(id, dataset);
76         return createSender(context, EXTRA_DATASET_ID, id);
77     }
78 
79     /**
80      * Creates an {@link IntentSender} with the given unique id for the given fill response.
81      */
createSender(Context context, int id, CannedFillResponse response)82     public static IntentSender createSender(Context context, int id,
83             CannedFillResponse response) {
84         Preconditions.checkArgument(id > 0, "id must be positive");
85         Preconditions.checkState(sResponses.get(id) == null, "already have id");
86         sResponses.put(id, response);
87         return createSender(context, EXTRA_RESPONSE_ID, id);
88     }
89 
createSender(Context context, String extraName, int id)90     private static IntentSender createSender(Context context, String extraName, int id) {
91         final Intent intent = new Intent(context, AuthenticationActivity.class);
92         intent.putExtra(extraName, id);
93         final PendingIntent pendingIntent = PendingIntent.getActivity(context, id, intent, 0);
94         sPendingIntents.add(pendingIntent);
95         return pendingIntent.getIntentSender();
96     }
97 
98     /**
99      * Creates an {@link IntentSender} with the given unique id.
100      */
createSender(Context context, int id)101     public static IntentSender createSender(Context context, int id) {
102         Preconditions.checkArgument(id > 0, "id must be positive");
103         return PendingIntent
104                 .getActivity(context, id, new Intent(context, AuthenticationActivity.class), 0)
105                 .getIntentSender();
106     }
107 
getData()108     public static Bundle getData() {
109         final Bundle data = sData;
110         sData = null;
111         return data;
112     }
113 
114     /**
115      * Sets the value that's passed to {@link Activity#setResult(int, Intent)} when on
116      * {@link Activity#onCreate(Bundle)}.
117      */
setResultCode(int resultCode)118     public static void setResultCode(int resultCode) {
119         synchronized (sLock) {
120             sResultCode = resultCode;
121         }
122     }
123 
124     @Override
onCreate(Bundle savedInstanceState)125     protected void onCreate(Bundle savedInstanceState) {
126         super.onCreate(savedInstanceState);
127 
128         // We should get the assist structure...
129         final AssistStructure structure = getIntent().getParcelableExtra(
130                 AutofillManager.EXTRA_ASSIST_STRUCTURE);
131         assertWithMessage("structure not called").that(structure).isNotNull();
132 
133         // and the bundle
134         sData = getIntent().getBundleExtra(AutofillManager.EXTRA_CLIENT_STATE);
135         final CannedFillResponse response =
136                 sResponses.get(getIntent().getIntExtra(EXTRA_RESPONSE_ID, 0));
137         final CannedDataset dataset =
138                 sDatasets.get(getIntent().getIntExtra(EXTRA_DATASET_ID, 0));
139 
140         final Parcelable result;
141 
142         if (response != null) {
143             result = response.asFillResponse((id) -> Helper.findNodeByResourceId(structure, id));
144         } else if (dataset != null) {
145             result = dataset.asDataset((id) -> Helper.findNodeByResourceId(structure, id));
146         } else {
147             throw new IllegalStateException("no dataset or response");
148         }
149 
150         // Pass on the auth result
151         final Intent intent = new Intent();
152         intent.putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, result);
153         final int resultCode;
154         synchronized (sLock) {
155             resultCode = sResultCode;
156         }
157         Log.d(TAG, "Returning code " + resultCode);
158         setResult(resultCode, intent);
159 
160         // Done
161         finish();
162     }
163 }
164