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.data.source.local; 17 18 import android.content.pm.PackageInfo; 19 import android.content.pm.PackageManager; 20 import android.support.annotation.NonNull; 21 22 import com.example.android.autofill.service.data.DataCallback; 23 import com.example.android.autofill.service.data.source.DalService; 24 import com.example.android.autofill.service.data.source.DigitalAssetLinksDataSource; 25 import com.example.android.autofill.service.model.DalCheck; 26 import com.example.android.autofill.service.model.DalInfo; 27 import com.example.android.autofill.service.util.SecurityHelper; 28 import com.google.common.net.InternetDomainName; 29 30 import java.util.HashMap; 31 32 import retrofit2.Call; 33 import retrofit2.Callback; 34 import retrofit2.Response; 35 import retrofit2.Retrofit; 36 37 import static com.example.android.autofill.service.util.Util.DalCheckRequirement; 38 import static com.example.android.autofill.service.util.Util.DalCheckRequirement.AllUrls; 39 import static com.example.android.autofill.service.util.Util.DalCheckRequirement.Disabled; 40 import static com.example.android.autofill.service.util.Util.DalCheckRequirement.LoginOnly; 41 import static com.example.android.autofill.service.util.Util.logd; 42 43 44 /** 45 * Singleton repository that caches the result of Digital Asset Links checks. 46 */ 47 public class DigitalAssetLinksRepository implements DigitalAssetLinksDataSource { 48 private static final String DAL_BASE_URL = "https://digitalassetlinks.googleapis.com"; 49 private static final String PERMISSION_GET_LOGIN_CREDS = "common.get_login_creds"; 50 private static final String PERMISSION_HANDLE_ALL_URLS = "common.handle_all_urls"; 51 private static DigitalAssetLinksRepository sInstance; 52 53 private final PackageManager mPackageManager; 54 private final DalService mDalService; 55 private final HashMap<DalInfo, DalCheck> mCache; 56 57 private DigitalAssetLinksRepository(PackageManager packageManager) { 58 mPackageManager = packageManager; 59 mCache = new HashMap<>(); 60 mDalService = new Retrofit.Builder() 61 .baseUrl(DAL_BASE_URL) 62 .build() 63 .create(DalService.class); 64 } 65 66 public static DigitalAssetLinksRepository getInstance(PackageManager packageManager) { 67 if (sInstance == null) { 68 sInstance = new DigitalAssetLinksRepository(packageManager); 69 } 70 return sInstance; 71 } 72 73 public static String getCanonicalDomain(String domain) { 74 InternetDomainName idn = InternetDomainName.from(domain); 75 while (idn != null && !idn.isTopPrivateDomain()) { 76 idn = idn.parent(); 77 } 78 return idn == null ? null : idn.toString(); 79 } 80 81 @Override 82 public void clear() { 83 mCache.clear(); 84 } 85 86 public void checkValid(DalCheckRequirement dalCheckRequirement, DalInfo dalInfo, 87 DataCallback<DalCheck> dalCheckDataCallback) { 88 if (dalCheckRequirement.equals(Disabled)) { 89 DalCheck dalCheck = new DalCheck(); 90 dalCheck.linked = true; 91 dalCheckDataCallback.onLoaded(dalCheck); 92 return; 93 } 94 95 DalCheck dalCheck = mCache.get(dalInfo); 96 if (dalCheck != null) { 97 dalCheckDataCallback.onLoaded(dalCheck); 98 return; 99 } 100 String packageName = dalInfo.getPackageName(); 101 String webDomain = dalInfo.getWebDomain(); 102 103 final String fingerprint; 104 try { 105 PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, 106 PackageManager.GET_SIGNATURES); 107 fingerprint = SecurityHelper.getFingerprint(packageInfo, packageName); 108 } catch (Exception e) { 109 dalCheckDataCallback.onDataNotAvailable("Error getting fingerprint for %s", 110 packageName); 111 return; 112 } 113 logd("validating domain %s for pkg %s and fingerprint %s.", webDomain, 114 packageName, fingerprint); 115 mDalService.check(webDomain, PERMISSION_GET_LOGIN_CREDS, packageName, fingerprint).enqueue( 116 new Callback<DalCheck>() { 117 @Override 118 public void onResponse(@NonNull Call<DalCheck> call, 119 @NonNull Response<DalCheck> response) { 120 DalCheck dalCheck = response.body(); 121 if (dalCheck == null || !dalCheck.linked) { 122 // get_login_creds check failed, so try handle_all_urls check 123 if (dalCheckRequirement.equals(LoginOnly)) { 124 dalCheckDataCallback.onDataNotAvailable( 125 "DAL: Login creds check failed."); 126 } else if (dalCheckRequirement.equals(AllUrls)) { 127 mDalService.check(webDomain, PERMISSION_HANDLE_ALL_URLS, 128 packageName, fingerprint).enqueue(new Callback<DalCheck>() { 129 @Override 130 public void onResponse(@NonNull Call<DalCheck> call, 131 @NonNull Response<DalCheck> response) { 132 DalCheck dalCheck = response.body(); 133 mCache.put(dalInfo, dalCheck); 134 dalCheckDataCallback.onLoaded(dalCheck); 135 } 136 137 @Override 138 public void onFailure(@NonNull Call<DalCheck> call, 139 @NonNull Throwable t) { 140 dalCheckDataCallback.onDataNotAvailable(t.getMessage()); 141 } 142 }); 143 } 144 } else { 145 // get_login_creds check succeeded, so we're finished. 146 mCache.put(dalInfo, dalCheck); 147 dalCheckDataCallback.onLoaded(dalCheck); 148 } 149 } 150 151 @Override 152 public void onFailure(@NonNull Call<DalCheck> call, @NonNull Throwable t) { 153 // get_login_creds check failed, so try handle_all_urls check. 154 mDalService.check(webDomain, PERMISSION_HANDLE_ALL_URLS, packageName, 155 fingerprint); 156 } 157 }); 158 } 159 } 160