Home | History | Annotate | Download | only in service
      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