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 package com.example.android.autofill.service; 17 18 import android.app.assist.AssistStructure; 19 import android.content.Context; 20 import android.content.IntentSender; 21 import android.content.SharedPreferences; 22 import android.os.CancellationSignal; 23 import android.service.autofill.AutofillService; 24 import android.service.autofill.FillCallback; 25 import android.service.autofill.FillContext; 26 import android.service.autofill.FillRequest; 27 import android.service.autofill.FillResponse; 28 import android.service.autofill.SaveCallback; 29 import android.service.autofill.SaveRequest; 30 import android.support.annotation.NonNull; 31 import android.view.autofill.AutofillManager; 32 import android.widget.RemoteViews; 33 34 import com.example.android.autofill.service.data.AutofillDataBuilder; 35 import com.example.android.autofill.service.data.ClientAutofillDataBuilder; 36 import com.example.android.autofill.service.data.ClientViewMetadata; 37 import com.example.android.autofill.service.data.ClientViewMetadataBuilder; 38 import com.example.android.autofill.service.data.DataCallback; 39 import com.example.android.autofill.service.data.adapter.DatasetAdapter; 40 import com.example.android.autofill.service.data.adapter.ResponseAdapter; 41 import com.example.android.autofill.service.data.source.DefaultFieldTypesSource; 42 import com.example.android.autofill.service.data.source.PackageVerificationDataSource; 43 import com.example.android.autofill.service.data.source.local.DefaultFieldTypesLocalJsonSource; 44 import com.example.android.autofill.service.data.source.local.DigitalAssetLinksRepository; 45 import com.example.android.autofill.service.data.source.local.LocalAutofillDataSource; 46 import com.example.android.autofill.service.data.source.local.SharedPrefsPackageVerificationRepository; 47 import com.example.android.autofill.service.data.source.local.dao.AutofillDao; 48 import com.example.android.autofill.service.data.source.local.db.AutofillDatabase; 49 import com.example.android.autofill.service.model.DalCheck; 50 import com.example.android.autofill.service.model.DalInfo; 51 import com.example.android.autofill.service.model.DatasetWithFilledAutofillFields; 52 import com.example.android.autofill.service.model.FieldTypeWithHeuristics; 53 import com.example.android.autofill.service.settings.MyPreferences; 54 import com.example.android.autofill.service.util.AppExecutors; 55 import com.example.android.autofill.service.util.Util; 56 import com.google.gson.GsonBuilder; 57 58 import java.util.HashMap; 59 import java.util.List; 60 import java.util.concurrent.atomic.AtomicReference; 61 62 import static com.example.android.autofill.service.util.Util.DalCheckRequirement; 63 import static com.example.android.autofill.service.util.Util.bundleToString; 64 import static com.example.android.autofill.service.util.Util.dumpStructure; 65 import static com.example.android.autofill.service.util.Util.logVerboseEnabled; 66 import static com.example.android.autofill.service.util.Util.logd; 67 import static com.example.android.autofill.service.util.Util.loge; 68 import static com.example.android.autofill.service.util.Util.logv; 69 import static com.example.android.autofill.service.util.Util.logw; 70 import static java.util.stream.Collectors.toList; 71 72 public class MyAutofillService extends AutofillService { 73 74 private LocalAutofillDataSource mLocalAutofillDataSource; 75 private DigitalAssetLinksRepository mDalRepository; 76 private PackageVerificationDataSource mPackageVerificationRepository; 77 private AutofillDataBuilder mAutofillDataBuilder; 78 private ResponseAdapter mResponseAdapter; 79 private ClientViewMetadata mClientViewMetadata; 80 private MyPreferences mPreferences; 81 82 @Override 83 public void onCreate() { 84 super.onCreate(); 85 mPreferences = MyPreferences.getInstance(this); 86 Util.setLoggingLevel(mPreferences.getLoggingLevel()); 87 SharedPreferences localAfDataSourceSharedPrefs = 88 getSharedPreferences(LocalAutofillDataSource.SHARED_PREF_KEY, Context.MODE_PRIVATE); 89 DefaultFieldTypesSource defaultFieldTypesSource = 90 DefaultFieldTypesLocalJsonSource.getInstance(getResources(), 91 new GsonBuilder().create()); 92 AutofillDao autofillDao = AutofillDatabase.getInstance(this, 93 defaultFieldTypesSource, new AppExecutors()).autofillDao(); 94 mLocalAutofillDataSource = LocalAutofillDataSource.getInstance(localAfDataSourceSharedPrefs, 95 autofillDao, new AppExecutors()); 96 mDalRepository = DigitalAssetLinksRepository.getInstance(getPackageManager()); 97 mPackageVerificationRepository = SharedPrefsPackageVerificationRepository.getInstance(this); 98 } 99 100 @Override 101 public void onFillRequest(@NonNull FillRequest request, 102 @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback) { 103 List<FillContext> fillContexts = request.getFillContexts(); 104 List<AssistStructure> structures = 105 fillContexts.stream().map(FillContext::getStructure).collect(toList()); 106 AssistStructure latestStructure = fillContexts.get(fillContexts.size() - 1).getStructure(); 107 ClientParser parser = new ClientParser(structures); 108 109 // Check user's settings for authenticating Responses and Datasets. 110 boolean responseAuth = mPreferences.isResponseAuth(); 111 boolean datasetAuth = mPreferences.isDatasetAuth(); 112 boolean manual = (request.getFlags() & FillRequest.FLAG_MANUAL_REQUEST) != 0; 113 mLocalAutofillDataSource.getFieldTypeByAutofillHints( 114 new DataCallback<HashMap<String, FieldTypeWithHeuristics>>() { 115 @Override 116 public void onLoaded(HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint) { 117 DatasetAdapter datasetAdapter = new DatasetAdapter(parser); 118 ClientViewMetadataBuilder clientViewMetadataBuilder = 119 new ClientViewMetadataBuilder(parser, fieldTypesByAutofillHint); 120 mClientViewMetadata = clientViewMetadataBuilder.buildClientViewMetadata(); 121 mResponseAdapter = new ResponseAdapter(MyAutofillService.this, 122 mClientViewMetadata, getPackageName(), datasetAdapter); 123 String packageName = latestStructure.getActivityComponent().getPackageName(); 124 if (!mPackageVerificationRepository.putPackageSignatures(packageName)) { 125 callback.onFailure(getString(R.string.invalid_package_signature)); 126 return; 127 } 128 if (logVerboseEnabled()) { 129 logv("onFillRequest(): clientState=%s", 130 bundleToString(request.getClientState())); 131 dumpStructure(latestStructure); 132 } 133 cancellationSignal.setOnCancelListener(() -> 134 logw("Cancel autofill not implemented in this sample.") 135 ); 136 fetchDataAndGenerateResponse(fieldTypesByAutofillHint, responseAuth, 137 datasetAuth, manual, callback); 138 } 139 140 @Override 141 public void onDataNotAvailable(String msg, Object... params) { 142 143 } 144 }); 145 } 146 147 private void fetchDataAndGenerateResponse( 148 HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint, boolean responseAuth, 149 boolean datasetAuth, boolean manual, FillCallback callback) { 150 if (responseAuth) { 151 // If the entire Autofill Response is authenticated, AuthActivity is used 152 // to generate Response. 153 IntentSender sender = AuthActivity.getAuthIntentSenderForResponse(this); 154 RemoteViews remoteViews = RemoteViewsHelper.viewsWithAuth(getPackageName(), 155 getString(R.string.autofill_sign_in_prompt)); 156 FillResponse response = mResponseAdapter.buildResponse(sender, remoteViews); 157 if (response != null) { 158 callback.onSuccess(response); 159 } 160 } else { 161 mLocalAutofillDataSource.getAutofillDatasets(mClientViewMetadata.getAllHints(), 162 new DataCallback<List<DatasetWithFilledAutofillFields>>() { 163 @Override 164 public void onLoaded(List<DatasetWithFilledAutofillFields> datasets) { 165 if ((datasets == null || datasets.isEmpty()) && manual) { 166 IntentSender sender = ManualActivity 167 .getManualIntentSenderForResponse(MyAutofillService.this); 168 RemoteViews remoteViews = RemoteViewsHelper.viewsWithNoAuth( 169 getPackageName(), 170 getString(R.string.autofill_manual_prompt)); 171 FillResponse response = mResponseAdapter.buildManualResponse(sender, 172 remoteViews); 173 if (response != null) { 174 callback.onSuccess(response); 175 } 176 } else { 177 FillResponse response = mResponseAdapter.buildResponse( 178 fieldTypesByAutofillHint, datasets, datasetAuth); 179 callback.onSuccess(response); 180 } 181 } 182 183 @Override 184 public void onDataNotAvailable(String msg, Object... params) { 185 logw(msg, params); 186 callback.onFailure(String.format(msg, params)); 187 } 188 }); 189 } 190 } 191 192 @Override 193 public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) { 194 List<FillContext> fillContexts = request.getFillContexts(); 195 List<AssistStructure> structures = 196 fillContexts.stream().map(FillContext::getStructure).collect(toList()); 197 AssistStructure latestStructure = fillContexts.get(fillContexts.size() - 1).getStructure(); 198 ClientParser parser = new ClientParser(structures); 199 mLocalAutofillDataSource.getFieldTypeByAutofillHints( 200 new DataCallback<HashMap<String, FieldTypeWithHeuristics>>() { 201 @Override 202 public void onLoaded( 203 HashMap<String, FieldTypeWithHeuristics> fieldTypesByAutofillHint) { 204 mAutofillDataBuilder = new ClientAutofillDataBuilder( 205 fieldTypesByAutofillHint, getPackageName(), parser); 206 ClientViewMetadataBuilder clientViewMetadataBuilder = 207 new ClientViewMetadataBuilder(parser, fieldTypesByAutofillHint); 208 mClientViewMetadata = clientViewMetadataBuilder.buildClientViewMetadata(); 209 String packageName = latestStructure.getActivityComponent().getPackageName(); 210 if (!mPackageVerificationRepository.putPackageSignatures(packageName)) { 211 callback.onFailure(getString(R.string.invalid_package_signature)); 212 return; 213 } 214 if (logVerboseEnabled()) { 215 logv("onSaveRequest(): clientState=%s", 216 bundleToString(request.getClientState())); 217 } 218 dumpStructure(latestStructure); 219 checkWebDomainAndBuildAutofillData(packageName, callback); 220 } 221 222 @Override 223 public void onDataNotAvailable(String msg, Object... params) { 224 loge("Should not happen - could not find field types."); 225 } 226 }); 227 } 228 229 private void checkWebDomainAndBuildAutofillData(String packageName, SaveCallback callback) { 230 String webDomain; 231 try { 232 webDomain = mClientViewMetadata.getWebDomain(); 233 } catch (SecurityException e) { 234 logw(e.getMessage()); 235 callback.onFailure(getString(R.string.security_exception)); 236 return; 237 } 238 if (webDomain != null && webDomain.length() > 0) { 239 DalCheckRequirement req = mPreferences.getDalCheckRequirement(); 240 mDalRepository.checkValid(req, new DalInfo(webDomain, packageName), 241 new DataCallback<DalCheck>() { 242 @Override 243 public void onLoaded(DalCheck dalCheck) { 244 if (dalCheck.linked) { 245 logd("Domain %s is valid for %s", webDomain, packageName); 246 buildAndSaveAutofillData(); 247 } else { 248 loge("Could not associate web domain %s with app %s", 249 webDomain, packageName); 250 callback.onFailure(getString(R.string.dal_exception)); 251 } 252 } 253 254 @Override 255 public void onDataNotAvailable(String msg, Object... params) { 256 logw(msg, params); 257 callback.onFailure(getString(R.string.dal_exception)); 258 } 259 }); 260 } else { 261 logd("no web domain"); 262 buildAndSaveAutofillData(); 263 } 264 } 265 266 private void buildAndSaveAutofillData() { 267 int datasetNumber = mLocalAutofillDataSource.getDatasetNumber(); 268 List<DatasetWithFilledAutofillFields> datasetsWithFilledAutofillFields = 269 mAutofillDataBuilder.buildDatasetsByPartition(datasetNumber); 270 mLocalAutofillDataSource.saveAutofillDatasets(datasetsWithFilledAutofillFields); 271 } 272 273 @Override 274 public void onConnected() { 275 logd("onConnected"); 276 } 277 278 @Override 279 public void onDisconnected() { 280 logd("onDisconnected"); 281 } 282 } 283