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