Autofill sample: Adding pkg signature check.

Also, minor refactor to AutofillDataSource to make
getInstance() parameter-less.

Bug: 38182790
Test: manual
Change-Id: Ib8e6780dfde2e263d9648d78df6b7e70477cf146
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java
index c6615ff..015dc68 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AuthActivity.java
@@ -15,7 +15,6 @@
  */
 package com.example.android.autofillframework.multidatasetservice;
 
-import android.app.Activity;
 import android.app.PendingIntent;
 import android.app.assist.AssistStructure;
 import android.content.Context;
@@ -131,8 +130,8 @@
         int saveTypes = autofillFields.getSaveType();
         mReplyIntent = new Intent();
         HashMap<String, FilledAutofillFieldCollection> clientFormDataMap =
-                SharedPrefsAutofillRepository.getInstance(this).getFilledAutofillFieldCollection
-                        (autofillFields.getFocusedHints(), autofillFields.getAllHints());
+                SharedPrefsAutofillRepository.getInstance().getFilledAutofillFieldCollection
+                        (this, autofillFields.getFocusedHints(), autofillFields.getAllHints());
         if (forResponse) {
             setResponseIntent(AutofillHelper.newResponse
                     (this, false, autofillFields, clientFormDataMap));
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java
index 75cc849..1e76f6e 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/AutofillHelper.java
@@ -29,7 +29,6 @@
 import com.example.android.autofillframework.R;
 import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Set;
 
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java
index 19cf2ae..047ee67 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.java
@@ -29,9 +29,11 @@
 import android.util.Log;
 import android.view.autofill.AutofillId;
 import android.widget.RemoteViews;
+import android.widget.Toast;
 
 import com.example.android.autofillframework.R;
 import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsAutofillRepository;
+import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsPackageVerificationRepository;
 import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
 import com.example.android.autofillframework.multidatasetservice.settings.MyPreferences;
 
@@ -49,6 +51,13 @@
             FillCallback callback) {
         AssistStructure structure = request.getFillContexts()
                 .get(request.getFillContexts().size() - 1).getStructure();
+        String packageName = structure.getActivityComponent().getPackageName();
+        if (!SharedPrefsPackageVerificationRepository.getInstance()
+                .putPackageSignatures(getApplicationContext(), packageName)) {
+            Toast.makeText(getApplicationContext(), R.string.invalid_package_signature,
+                    Toast.LENGTH_SHORT).show();
+            return;
+        }
         final Bundle data = request.getClientState();
         Log.d(TAG, "onFillRequest(): data=" + bundleToString(data));
 
@@ -79,8 +88,8 @@
         } else {
             boolean datasetAuth = MyPreferences.getInstance(this).isDatasetAuth();
             HashMap<String, FilledAutofillFieldCollection> clientFormDataMap =
-                    SharedPrefsAutofillRepository.getInstance(this).getFilledAutofillFieldCollection
-                            (autofillFields.getFocusedHints(), autofillFields.getAllHints());
+                    SharedPrefsAutofillRepository.getInstance().getFilledAutofillFieldCollection
+                            (this, autofillFields.getFocusedHints(), autofillFields.getAllHints());
             FillResponse response = AutofillHelper.newResponse
                     (this, datasetAuth, autofillFields, clientFormDataMap);
             callback.onSuccess(response);
@@ -91,12 +100,20 @@
     public void onSaveRequest(SaveRequest request, SaveCallback callback) {
         List<FillContext> context = request.getFillContexts();
         final AssistStructure structure = context.get(context.size() - 1).getStructure();
+        String packageName = structure.getActivityComponent().getPackageName();
+        if (!SharedPrefsPackageVerificationRepository.getInstance()
+                .putPackageSignatures(getApplicationContext(), packageName)) {
+            Toast.makeText(getApplicationContext(), R.string.invalid_package_signature,
+                    Toast.LENGTH_SHORT).show();
+            return;
+        }
         final Bundle data = request.getClientState();
         Log.d(TAG, "onSaveRequest(): data=" + bundleToString(data));
         StructureParser parser = new StructureParser(structure);
         parser.parseForSave();
         FilledAutofillFieldCollection filledAutofillFieldCollection = parser.getClientFormData();
-        SharedPrefsAutofillRepository.getInstance(this).saveFilledAutofillFieldCollection(filledAutofillFieldCollection);
+        SharedPrefsAutofillRepository.getInstance()
+                .saveFilledAutofillFieldCollection(this, filledAutofillFieldCollection);
     }
 
     @Override
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillRepository.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillDataSource.java
similarity index 71%
rename from input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillRepository.java
rename to input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillDataSource.java
index 7ee9555..99d1c26 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillRepository.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/AutofillDataSource.java
@@ -15,27 +15,30 @@
  */
 package com.example.android.autofillframework.multidatasetservice.datasource;
 
+import android.content.Context;
+
 import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
 
 import java.util.HashMap;
 import java.util.List;
 
-public interface AutofillRepository {
+public interface AutofillDataSource {
 
     /**
-     * Gets saved FilledAutofillFieldCollection that contains some objects that can autofill fields with these
-     * {@code autofillHints}.
+     * Gets saved FilledAutofillFieldCollection that contains some objects that can autofill fields
+     * with these {@code autofillHints}.
      */
-    HashMap<String, FilledAutofillFieldCollection> getFilledAutofillFieldCollection(List<String> focusedAutofillHints,
-            List<String> allAutofillHints);
+    HashMap<String, FilledAutofillFieldCollection> getFilledAutofillFieldCollection(Context context,
+            List<String> focusedAutofillHints, List<String> allAutofillHints);
 
     /**
      * Saves LoginCredential under this datasetName.
      */
-    void saveFilledAutofillFieldCollection(FilledAutofillFieldCollection filledAutofillFieldCollection);
+    void saveFilledAutofillFieldCollection(Context context,
+            FilledAutofillFieldCollection filledAutofillFieldCollection);
 
     /**
      * Clears all data.
      */
-    void clear();
+    void clear(Context context);
 }
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/PackageVerificationDataSource.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/PackageVerificationDataSource.java
new file mode 100644
index 0000000..129001d
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/PackageVerificationDataSource.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.autofillframework.multidatasetservice.datasource;
+
+import android.content.Context;
+
+public interface PackageVerificationDataSource {
+
+    /**
+     * Verifies that the signatures in the passed {@code Context} match what is currently in
+     * storage. If there are no current signatures in storage for this packageName, it will store
+     * the signatures from the passed {@code Context}.
+     *
+     * @return {@code true} if signatures for this packageName are not currently in storage
+     * or if the signatures in the passed {@code Context} match what is currently in storage;
+     * {@code false} if the signatures in the passed {@code Context} do not match what is
+     * currently in storage or if an {@code Exception} was thrown while generating the signatures.
+     */
+    boolean putPackageSignatures(Context context, String packageName);
+
+    /**
+     * Clears all signature data currently in storage.
+     */
+    void clear(Context context);
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java
index 9d29fb3..7b55ef2 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsAutofillRepository.java
@@ -16,7 +16,6 @@
 package com.example.android.autofillframework.multidatasetservice.datasource;
 
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.util.ArraySet;
 
 import com.example.android.autofillframework.multidatasetservice.model.FilledAutofillFieldCollection;
@@ -32,36 +31,33 @@
  * Disclaimer: you should not store sensitive fields like user data unencrypted. This is done
  * here only for simplicity and learning purposes.
  */
-public class SharedPrefsAutofillRepository implements AutofillRepository {
-    private static final String SHARED_PREF_KEY = "com.example.android.autofillframework.service";
+public class SharedPrefsAutofillRepository implements AutofillDataSource {
+    private static final String SHARED_PREF_KEY = "com.example.android.autofillframework"
+            + ".multidatasetservice.datasource.AutofillDataSource";
     private static final String CLIENT_FORM_DATA_KEY = "loginCredentialDatasets";
     private static final String DATASET_NUMBER_KEY = "datasetNumber";
-
     private static SharedPrefsAutofillRepository sInstance;
 
-    private final SharedPreferences mPrefs;
-
-    private SharedPrefsAutofillRepository(Context context) {
-        mPrefs = context.getApplicationContext()
-                .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE);
+    private SharedPrefsAutofillRepository() {
     }
 
-    public static SharedPrefsAutofillRepository getInstance(Context context) {
+    public static SharedPrefsAutofillRepository getInstance() {
         if (sInstance == null) {
-            sInstance = new SharedPrefsAutofillRepository(context);
+            sInstance = new SharedPrefsAutofillRepository();
         }
         return sInstance;
     }
 
     @Override
-    public HashMap<String, FilledAutofillFieldCollection> getFilledAutofillFieldCollection(List<String> focusedAutofillHints,
-            List<String> allAutofillHints) {
+    public HashMap<String, FilledAutofillFieldCollection> getFilledAutofillFieldCollection(
+            Context context, List<String> focusedAutofillHints, List<String> allAutofillHints) {
         boolean hasDataForFocusedAutofillHints = false;
         HashMap<String, FilledAutofillFieldCollection> clientFormDataMap = new HashMap<>();
-        Set<String> clientFormDataStringSet = getAllAutofillDataStringSet();
+        Set<String> clientFormDataStringSet = getAllAutofillDataStringSet(context);
         for (String clientFormDataString : clientFormDataStringSet) {
             Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
-            FilledAutofillFieldCollection filledAutofillFieldCollection = gson.fromJson(clientFormDataString, FilledAutofillFieldCollection.class);
+            FilledAutofillFieldCollection filledAutofillFieldCollection =
+                    gson.fromJson(clientFormDataString, FilledAutofillFieldCollection.class);
             if (filledAutofillFieldCollection != null) {
                 if (filledAutofillFieldCollection.helpsWithHints(focusedAutofillHints)) {
                     // Saved data has data relevant to at least 1 of the hints associated with the
@@ -71,7 +67,8 @@
                 if (filledAutofillFieldCollection.helpsWithHints(allAutofillHints)) {
                     // Saved data has data relevant to at least 1 of these hints associated with any
                     // of the Views in the hierarchy.
-                    clientFormDataMap.put(filledAutofillFieldCollection.getDatasetName(), filledAutofillFieldCollection);
+                    clientFormDataMap.put(filledAutofillFieldCollection.getDatasetName(),
+                            filledAutofillFieldCollection);
                 }
             }
         }
@@ -83,42 +80,61 @@
     }
 
     @Override
-    public void saveFilledAutofillFieldCollection(FilledAutofillFieldCollection filledAutofillFieldCollection) {
-        String datasetName = "dataset-" + getDatasetNumber();
+    public void saveFilledAutofillFieldCollection(Context context,
+            FilledAutofillFieldCollection filledAutofillFieldCollection) {
+        String datasetName = "dataset-" + getDatasetNumber(context);
         filledAutofillFieldCollection.setDatasetName(datasetName);
-        Set<String> allAutofillData = getAllAutofillDataStringSet();
+        Set<String> allAutofillData = getAllAutofillDataStringSet(context);
         Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
         allAutofillData.add(gson.toJson(filledAutofillFieldCollection));
-        saveAllAutofillDataStringSet(allAutofillData);
-        incrementDatasetNumber();
+        saveAllAutofillDataStringSet(context, allAutofillData);
+        incrementDatasetNumber(context);
     }
 
     @Override
-    public void clear() {
-        mPrefs.edit().remove(CLIENT_FORM_DATA_KEY).apply();
+    public void clear(Context context) {
+        context.getApplicationContext()
+                .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+                .edit()
+                .remove(CLIENT_FORM_DATA_KEY)
+                .remove(DATASET_NUMBER_KEY)
+                .apply();
     }
 
-    private Set<String> getAllAutofillDataStringSet() {
-        return mPrefs.getStringSet(CLIENT_FORM_DATA_KEY, new ArraySet<String>());
+    private Set<String> getAllAutofillDataStringSet(Context context) {
+        return context.getApplicationContext()
+                .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+                .getStringSet(CLIENT_FORM_DATA_KEY, new ArraySet<String>());
     }
 
-    private void saveAllAutofillDataStringSet(Set<String> allAutofillDataStringSet) {
-        mPrefs.edit().putStringSet(CLIENT_FORM_DATA_KEY, allAutofillDataStringSet).apply();
+    private void saveAllAutofillDataStringSet(Context context,
+            Set<String> allAutofillDataStringSet) {
+        context.getApplicationContext()
+                .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+                .edit()
+                .putStringSet(CLIENT_FORM_DATA_KEY, allAutofillDataStringSet)
+                .apply();
     }
 
     /**
      * For simplicity, datasets will be named in the form "dataset-X" where X means
      * this was the Xth dataset saved.
      */
-    private int getDatasetNumber() {
-        return mPrefs.getInt(DATASET_NUMBER_KEY, 0);
+    private int getDatasetNumber(Context context) {
+        return context.getApplicationContext()
+                .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+                .getInt(DATASET_NUMBER_KEY, 0);
     }
 
     /**
      * Every time a dataset is saved, this should be called to increment the dataset number.
      * (only important for this service's dataset naming scheme).
      */
-    private void incrementDatasetNumber() {
-        mPrefs.edit().putInt(DATASET_NUMBER_KEY, getDatasetNumber() + 1).apply();
+    private void incrementDatasetNumber(Context context) {
+        context.getApplicationContext()
+                .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+                .edit()
+                .putInt(DATASET_NUMBER_KEY, getDatasetNumber(context) + 1)
+                .apply();
     }
 }
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsPackageVerificationRepository.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsPackageVerificationRepository.java
new file mode 100644
index 0000000..b7bb582
--- /dev/null
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/datasource/SharedPrefsPackageVerificationRepository.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.example.android.autofillframework.multidatasetservice.datasource;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import static com.example.android.autofillframework.CommonUtil.TAG;
+
+public class SharedPrefsPackageVerificationRepository implements PackageVerificationDataSource {
+
+    private static final String SHARED_PREF_KEY = "com.example.android.autofillframework"
+            + ".multidatasetservice.datasource.PackageVerificationDataSource";
+    private static PackageVerificationDataSource sInstance;
+
+    private SharedPrefsPackageVerificationRepository() {
+    }
+
+    public static PackageVerificationDataSource getInstance() {
+        if (sInstance == null) {
+            sInstance = new SharedPrefsPackageVerificationRepository();
+        }
+        return sInstance;
+    }
+
+    @Override
+    public void clear(Context context) {
+        context.getApplicationContext().getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+                .edit()
+                .clear()
+                .apply();
+    }
+
+    @Override
+    public boolean putPackageSignatures(Context context, String packageName) {
+        String hash;
+        try {
+            hash = getCertificateHash(context, packageName);
+            Log.d(TAG, "Hash for " + packageName + ": " + hash);
+        } catch (Exception e) {
+            Log.w(TAG, "Error getting hash for " + packageName + ": " + e);
+            return false;
+        }
+
+        if (!containsSignatureForPackage(context, packageName)) {
+            // Storage does not yet contain signature for this package name.
+            context.getApplicationContext()
+                    .getSharedPreferences(SHARED_PREF_KEY, Context.MODE_PRIVATE)
+                    .edit()
+                    .putString(packageName, hash)
+                    .apply();
+            return true;
+        }
+        return containsMatchingSignatureForPackage(context, packageName, hash);
+    }
+
+    private boolean containsSignatureForPackage(Context context, String packageName) {
+        SharedPreferences prefs = context.getApplicationContext().getSharedPreferences(
+                SHARED_PREF_KEY, Context.MODE_PRIVATE);
+        return prefs.contains(packageName);
+    }
+
+    private boolean containsMatchingSignatureForPackage(Context context, String packageName,
+            String hash) {
+        SharedPreferences prefs = context.getApplicationContext().getSharedPreferences(
+                SHARED_PREF_KEY, Context.MODE_PRIVATE);
+        return hash.equals(prefs.getString(packageName, null));
+    }
+
+    private String getCertificateHash(Context context, String packageName)
+            throws Exception {
+        PackageManager pm = context.getPackageManager();
+        PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+        Signature[] signatures = packageInfo.signatures;
+        byte[] cert = signatures[0].toByteArray();
+        try (InputStream input = new ByteArrayInputStream(cert)) {
+            CertificateFactory factory = CertificateFactory.getInstance("X509");
+            X509Certificate x509 = (X509Certificate) factory.generateCertificate(input);
+            MessageDigest md = MessageDigest.getInstance("SHA256");
+            byte[] publicKey = md.digest(x509.getEncoded());
+            return toHexFormat(publicKey);
+        }
+    }
+
+    private String toHexFormat(byte[] bytes) {
+        StringBuilder builder = new StringBuilder(bytes.length * 2);
+        for (int i = 0; i < bytes.length; i++) {
+            String hex = Integer.toHexString(bytes[i]);
+            int length = hex.length();
+            if (length == 1) {
+                hex = "0" + hex;
+            }
+            if (length > 2) {
+                hex = hex.substring(length - 2, length);
+            }
+            builder.append(hex.toUpperCase());
+            if (i < (bytes.length - 1)) {
+                builder.append(':');
+            }
+        }
+        return builder.toString();
+    }
+}
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java
index fccb3b6..d50af53 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/model/FilledAutofillFieldCollection.java
@@ -78,6 +78,7 @@
      * Populates a {@link Dataset.Builder} with appropriate values for each {@link AutofillId}
      * in a {@code AutofillFieldMetadataCollection}.
      *
+     * <p>
      * In other words, it constructs an autofill
      * {@link Dataset.Builder} by applying saved values (from this {@code FilledAutofillFieldCollection})
      * to Views specified in a {@code AutofillFieldMetadataCollection}, which represents the current
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java
index bb6e831..34d63ef 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/MyPreferences.java
@@ -22,12 +22,9 @@
 import android.support.annotation.NonNull;
 
 public class MyPreferences {
-    private static final String TAG = "MyPreferences";
-
     private static final String RESPONSE_AUTH_KEY = "response_auth";
     private static final String DATASET_AUTH_KEY = "dataset_auth";
     private static final String MASTER_PASSWORD_KEY = "master_password";
-
     private static MyPreferences sInstance;
     private final SharedPreferences mPrefs;
 
diff --git a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java
index f013f78..5224bea 100644
--- a/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java
+++ b/input/autofill/AutofillFramework/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/settings/SettingsActivity.java
@@ -30,6 +30,7 @@
 
 import com.example.android.autofillframework.R;
 import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsAutofillRepository;
+import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsPackageVerificationRepository;
 
 public class SettingsActivity extends AppCompatActivity {
 
@@ -90,10 +91,10 @@
                 .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
-                        SharedPrefsAutofillRepository.getInstance
-                                (SettingsActivity.this).clear();
-                        MyPreferences.getInstance(SettingsActivity.this)
-                                .clearCredentials();
+                        SharedPrefsAutofillRepository.getInstance().clear(SettingsActivity.this);
+                        SharedPrefsPackageVerificationRepository.getInstance()
+                                .clear(SettingsActivity.this);
+                        MyPreferences.getInstance(SettingsActivity.this).clearCredentials();
                         dialog.dismiss();
                     }
                 })
@@ -149,7 +150,7 @@
 
     private void setupSettingsSwitch(int containerId, int labelId, int switchId, boolean checked,
             CompoundButton.OnCheckedChangeListener checkedChangeListener) {
-        ViewGroup container = (ViewGroup) findViewById(containerId);
+        ViewGroup container = findViewById(containerId);
         String switchLabel = ((TextView) container.findViewById(labelId)).getText().toString();
         final Switch switchView = container.findViewById(switchId);
         switchView.setContentDescription(switchLabel);
@@ -165,10 +166,11 @@
 
     private void setupSettingsButton(int containerId, int labelId, int imageViewId,
             final View.OnClickListener onClickListener) {
-        ViewGroup container = (ViewGroup) findViewById(containerId);
-        String buttonLabel = ((TextView) container.findViewById(labelId)).getText().toString();
-        final ImageView imageView = container.findViewById(imageViewId);
-        imageView.setContentDescription(buttonLabel);
+        ViewGroup container = findViewById(containerId);
+        TextView buttonLabel = container.findViewById(labelId);
+        String buttonLabelText = buttonLabel.getText().toString();
+        ImageView imageView = container.findViewById(imageViewId);
+        imageView.setContentDescription(buttonLabelText);
         container.setOnClickListener(onClickListener);
     }
 }
diff --git a/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml b/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml
index 89ec9d4..16a8781 100644
--- a/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml
+++ b/input/autofill/AutofillFramework/Application/src/main/res/values/strings.xml
@@ -66,6 +66,7 @@
     <string name="submit_label">Submit</string>
     <string name="cc_exp_month_description">Credit Card Expiration Month</string>
     <string name="cc_exp_year_description">Credit Card Expiration Year</string>
+    <string name="invalid_package_signature">Invalid package signature</string>
     <string name="edittext_login_info">This is a sample login page that uses standard EditTexts
         from the UI toolkit. EditTexts are already optimized for autofill so extra autofill-specific
         code is almost never needed.
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.kt
index f74c587..704d17a 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.kt
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/MyAutofillService.kt
@@ -23,19 +23,24 @@
 import android.service.autofill.SaveCallback
 import android.service.autofill.SaveRequest
 import android.util.Log
-import android.view.autofill.AutofillId
+import android.widget.Toast
 import com.example.android.autofillframework.CommonUtil.TAG
 import com.example.android.autofillframework.CommonUtil.bundleToString
 import com.example.android.autofillframework.R
 import com.example.android.autofillframework.multidatasetservice.datasource.SharedPrefsAutofillRepository
 import com.example.android.autofillframework.multidatasetservice.settings.MyPreferences
-import java.util.Arrays
 
 class MyAutofillService : AutofillService() {
 
     override fun onFillRequest(request: FillRequest, cancellationSignal: CancellationSignal,
             callback: FillCallback) {
-        val structure = request.getFillContexts().get(request.getFillContexts().size - 1).structure
+        val structure = request.fillContexts[request.fillContexts.size - 1].structure
+        val packageName = structure.activityComponent.packageName
+        if (!PackageVerifier.isValidPackage(applicationContext, packageName)) {
+            Toast.makeText(applicationContext, R.string.invalid_package_signature,
+                    Toast.LENGTH_SHORT).show()
+            return
+        }
         val data = request.clientState
         Log.d(TAG, "onFillRequest(): data=" + bundleToString(data))
         cancellationSignal.setOnCancelListener { Log.w(TAG, "Cancel autofill not implemented in this sample.") }
@@ -68,6 +73,12 @@
     override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) {
         val context = request.fillContexts
         val structure = context[context.size - 1].structure
+        val packageName = structure.activityComponent.packageName
+        if (!PackageVerifier.isValidPackage(applicationContext, packageName)) {
+            Toast.makeText(applicationContext, R.string.invalid_package_signature,
+                    Toast.LENGTH_SHORT).show()
+            return
+        }
         val data = request.clientState
         Log.d(TAG, "onSaveRequest(): data=" + bundleToString(data))
         val parser = StructureParser(structure)
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/PackageVerifier.kt b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/PackageVerifier.kt
new file mode 100644
index 0000000..d059a23
--- /dev/null
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/java/com/example/android/autofillframework/multidatasetservice/PackageVerifier.kt
@@ -0,0 +1,98 @@
+package com.example.android.autofillframework.multidatasetservice
+
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.util.Log
+import com.example.android.autofillframework.CommonUtil.TAG
+import java.io.ByteArrayInputStream
+import java.security.MessageDigest
+import java.security.cert.CertificateFactory
+import java.security.cert.X509Certificate
+
+object PackageVerifier {
+
+
+    /**
+     * Verifies if a package is valid by matching its certificate with the previously stored
+     * certificate.
+     */
+    fun isValidPackage(context: Context, packageName: String): Boolean {
+        val hash: String
+        try {
+            hash = getCertificateHash(context, packageName)
+            Log.d(TAG, "Hash for $packageName: $hash")
+        } catch (e: Exception) {
+            Log.w(TAG, "Error getting hash for $packageName: $e")
+            return false
+        }
+
+        return verifyHash(context, packageName, hash)
+    }
+
+    private fun getCertificateHash(context: Context, packageName: String): String {
+        val pm = context.packageManager
+        val packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)
+        val signatures = packageInfo.signatures
+        val cert = signatures[0].toByteArray()
+        ByteArrayInputStream(cert).use { input ->
+            val factory = CertificateFactory.getInstance("X509")
+            val x509 = factory.generateCertificate(input) as X509Certificate
+            val md = MessageDigest.getInstance("SHA256")
+            val publicKey = md.digest(x509.encoded)
+            return toHexFormat(publicKey)
+        }
+    }
+
+    private fun toHexFormat(bytes: ByteArray): String {
+        val builder = StringBuilder(bytes.size * 2)
+        for (i in bytes.indices) {
+            var hex = Integer.toHexString(bytes[i].toInt())
+            val length = hex.length
+            if (length == 1) {
+                hex = "0" + hex
+            }
+            if (length > 2) {
+                hex = hex.substring(length - 2, length)
+            }
+            builder.append(hex.toUpperCase())
+            if (i < bytes.size - 1) {
+                builder.append(':')
+            }
+        }
+        return builder.toString()
+    }
+
+    private fun verifyHash(context: Context, packageName: String, hash: String): Boolean {
+        val prefs = context.applicationContext.getSharedPreferences(
+                "package-hashes", Context.MODE_PRIVATE)
+        if (!prefs.contains(packageName)) {
+            Log.d(TAG, "Creating intial hash for " + packageName)
+            prefs.edit().putString(packageName, hash).apply()
+            return true
+        }
+
+        val existingHash = prefs.getString(packageName, null)
+        if (hash != existingHash) {
+            Log.w(TAG, "hash mismatch for " + packageName + ": expected " + existingHash
+                    + ", got " + hash)
+            return false
+        }
+        return true
+    }
+}
\ No newline at end of file
diff --git a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml
index 1eca8c1..5a37019 100644
--- a/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml
+++ b/input/autofill/AutofillFramework/kotlinApp/Application/src/main/res/values/strings.xml
@@ -60,6 +60,7 @@
     <string name="submit_label">Submit</string>
     <string name="cc_exp_month_description">Credit Card Expiration Month</string>
     <string name="cc_exp_year_description">Credit Card Expiration Year</string>
+    <string name="invalid_package_signature">Invalid package signature</string>
     <string name="edittext_login_info">This is a sample login page that uses standard EditTexts
         from the UI toolkit. EditTexts are already optimized for autofill so extra autofill-specific
         code is almost never needed.