当前位置:   article > 正文

Android的Applink原理解析-8.0源码_intent filter verification service

intent filter verification service

           最近在做AppLink相关的,但是在所有配置搭建完毕之后,发现部分的手机无法实现AppLink的功能。比如华为,三星等等国外的手机;国内的oppo,vivo,小米等是可以的;

           那么是为什么呢?那么就从源码开始观察一下呗;想一想,先从PackageManagerService开始看。为什么呢?看名称就和包相关。其实另一方面,这边做过一个测试,当翻墙安装APP的一次之后,Applink每次都成功了(三星和华为)。那么我们多关注一下PackageManagerService这一块了。

    一、PackageManagerService

         1.installPackageLI方法里面,一路看下去直到startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);   看名称,就是做验证,

        2.看一下  startIntentFilterVerifications方法,里面跟随的 // 这个很重要

  1. private void startIntentFilterVerifications(int userId, boolean replacing,
  2. PackageParser.Package pkg) {
  3. if (mIntentFilterVerifierComponent == null) {
  4. Slog.w(TAG, "No IntentFilter verification will not be done as "
  5. + "there is no IntentFilterVerifier available!");
  6. return;
  7. }
  8. final int verifierUid = getPackageUid(
  9. mIntentFilterVerifierComponent.getPackageName(),
  10. MATCH_DEBUG_TRIAGED_MISSING,
  11. (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
  12. Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);//handler
  13. msg.obj = new IFVerificationParams(pkg, replacing, userId, verifierUid);//创建了认证的东西
  14. mHandler.sendMessage(msg);
  15. final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
  16. for (int i = 0; i < childCount; i++) {
  17. PackageParser.Package childPkg = pkg.childPackages.get(i);
  18. msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
  19. msg.obj = new IFVerificationParams(childPkg, replacing, userId, verifierUid);
  20. mHandler.sendMessage(msg);
  21. }
  22. }

    根据于START_INTENT_FILTER_VERIFICATIONS去查找一下

   3.startIntentFilterVerifications发送一个消息,随后调用verifyIntentFiltersIfNeeded进行验证

代码如下:  

  1. case START_INTENT_FILTER_VERIFICATIONS: {
  2. IFVerificationParams params = (IFVerificationParams) msg.obj;
  3. verifyIntentFiltersIfNeeded(params.userId, params.verifierUid,
  4. params.replacing, params.pkg);//这个一块又是一个重要的点,跟随下去
  5. break;
  6. }

   4.看一下verifyIntentFiltersIfNeeded方法:主要做了几个点:检查、搜集、验证。在检查阶段,首先判断是否有设置http/https scheme的Activity,并且是否满足设置了Intent.ACTION_DEFAULT与Intent.ACTION_VIEW,如果没有,则无需验证

代码如下:

  1. private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing,
  2. PackageParser.Package pkg) {
  3. int size = pkg.activities.size();
  4. if (size == 0) {
  5. if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
  6. "No activity, so no need to verify any IntentFilter!");
  7. return;
  8. }
  9. final boolean hasDomainURLs = hasDomainURLs(pkg);//判断这个是否含有Applink的url,以及配置
  10. if (!hasDomainURLs) {
  11. if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
  12. "No domain URLs, so no need to verify any IntentFilter!");
  13. return;
  14. }
  15. if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Checking for userId:" + userId
  16. + " if any IntentFilter from the " + size
  17. + " Activities needs verification ...");
  18. int count = 0;
  19. final String packageName = pkg.packageName;
  20. synchronized (mPackages) {
  21. // If this is a new install and we see that we've already run verification for this
  22. // package, we have nothing to do: it means the state was restored from backup.
  23. if (!replacing) {
  24. IntentFilterVerificationInfo ivi =
  25. mSettings.getIntentFilterVerificationLPr(packageName);
  26. if (ivi != null) {
  27. if (DEBUG_DOMAIN_VERIFICATION) {
  28. Slog.i(TAG, "Package " + packageName+ " already verified: status="
  29. + ivi.getStatusString());
  30. }
  31. return;
  32. }
  33. }
  34. // If any filters need to be verified, then all need to be.
  35. boolean needToVerify = false;//是否验证
  36. for (PackageParser.Activity a : pkg.activities) {
  37. for (ActivityIntentInfo filter : a.intents) {
  38. if (filter.needsVerification()//是否设置了autoverify
  39. && needsNetworkVerificationLPr(filter)) {
  40. if (DEBUG_DOMAIN_VERIFICATION) {
  41. Slog.d(TAG,
  42. "Intent filter needs verification, so processing all filters");
  43. }
  44. needToVerify = true;
  45. break;
  46. }
  47. }
  48. }
  49. if (needToVerify) {//收集
  50. final int verificationId = mIntentFilterVerificationToken++;
  51. for (PackageParser.Activity a : pkg.activities) {
  52. for (ActivityIntentInfo filter : a.intents) {
  53. if (filter.handlesWebUris(true) && needsNetworkVerificationLPr(filter)) {
  54. if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
  55. "Verification needed for IntentFilter:" + filter.toString());
  56. mIntentFilterVerifier.addOneIntentFilterVerification(
  57. verifierUid, userId, verificationId, filter, packageName);
  58. count++;
  59. }
  60. }
  61. }
  62. }
  63. }
  64. if (count > 0) {
  65. if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count
  66. + " IntentFilter verification" + (count > 1 ? "s" : "")
  67. + " for userId:" + userId);
  68. mIntentFilterVerifier.startVerifications(userId);//
  69. } else {
  70. if (DEBUG_DOMAIN_VERIFICATION) {
  71. Slog.d(TAG, "No filters or not all autoVerify for " + packageName);
  72. }
  73. }
  74. }

5.startVerifications这个方法继续下去:内部类IntentVerifierProxy的sendVerificationRequest 

  1. @Override
  2. public void startVerifications(int userId) {
  3. // Launch verifications requests
  4. int count = mCurrentIntentFilterVerifications.size();
  5. for (int n=0; n<count; n++) {
  6. int verificationId = mCurrentIntentFilterVerifications.get(n);
  7. final IntentFilterVerificationState ivs =
  8. mIntentFilterVerificationStates.get(verificationId);
  9. String packageName = ivs.getPackageName();
  10. ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
  11. final int filterCount = filters.size();
  12. ArraySet<String> domainsSet = new ArraySet<>();
  13. for (int m=0; m<filterCount; m++) {
  14. PackageParser.ActivityIntentInfo filter = filters.get(m);
  15. domainsSet.addAll(filter.getHostsList());
  16. }
  17. synchronized (mPackages) {
  18. if (mSettings.createIntentFilterVerificationIfNeededLPw(
  19. packageName, domainsSet) != null) {
  20. scheduleWriteSettingsLocked();
  21. }
  22. }
  23. sendVerificationRequest(verificationId, ivs);//关键
  24. }
  25. mCurrentIntentFilterVerifications.clear();
  26. }

6.看一下sendVerificationRequest方法代码:

  1. private void sendVerificationRequest(int verificationId, IntentFilterVerificationState ivs) {
  2. Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);//action,广播
  3. verificationIntent.putExtra(
  4. PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
  5. verificationId);
  6. verificationIntent.putExtra(
  7. PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
  8. getDefaultScheme());
  9. verificationIntent.putExtra(
  10. PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
  11. ivs.getHostsString());
  12. verificationIntent.putExtra(
  13. PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
  14. ivs.getPackageName());
  15. verificationIntent.setComponent(mIntentFilterVerifierComponent);
  16. verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
  17. DeviceIdleController.LocalService idleController = getDeviceIdleController();
  18. idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
  19. mIntentFilterVerifierComponent.getPackageName(), getVerificationTimeout(),
  20. UserHandle.USER_SYSTEM, true, "intent filter verifier");
  21. mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM);//发送
  22. if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
  23. "Sending IntentFilter verification broadcast");
  24. }

7.看一下广播发送的地方IntentFilterVerificationReceiver类里面onReceive方法:

  1. @Override
  2. public void onReceive(Context context, Intent intent) {
  3. final String action = intent.getAction();
  4. if (Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION.equals(action)) {
  5. Bundle inputExtras = intent.getExtras();
  6. if (inputExtras != null) {
  7. Intent serviceIntent = new Intent(context, DirectStatementService.class);//服务类
  8. serviceIntent.setAction(DirectStatementService.CHECK_ALL_ACTION);
  9. int verificationId = inputExtras.getInt(
  10. PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID);
  11. String scheme = inputExtras.getString(
  12. PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME);
  13. String hosts = inputExtras.getString(
  14. PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS);
  15. String packageName = inputExtras.getString(
  16. PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME);
  17. Bundle extras = new Bundle();
  18. extras.putString(DirectStatementService.EXTRA_RELATION, HANDLE_ALL_URLS_RELATION);
  19. String[] hostList = hosts.split(" ");
  20. if (hostList.length > MAX_HOSTS_PER_REQUEST) {
  21. Log.w(TAG, String.format(TOO_MANY_HOSTS_FORMAT,
  22. hostList.length, MAX_HOSTS_PER_REQUEST));
  23. sendErrorToPackageManager(context.getPackageManager(), verificationId);
  24. return;
  25. }
  26. ArrayList<String> finalHosts = new ArrayList<String>(hostList.length);
  27. try {
  28. ArrayList<String> sourceAssets = new ArrayList<String>();
  29. for (String host : hostList) {
  30. // "*.example.tld" is validated via https://example.tld
  31. if (host.startsWith("*.")) {
  32. host = host.substring(2);
  33. }
  34. sourceAssets.add(createWebAssetString(scheme, host));
  35. finalHosts.add(host);
  36. }
  37. extras.putStringArrayList(DirectStatementService.EXTRA_SOURCE_ASSET_DESCRIPTORS,
  38. sourceAssets);
  39. } catch (MalformedURLException e) {
  40. Log.w(TAG, "Error when processing input host: " + e.getMessage());
  41. sendErrorToPackageManager(context.getPackageManager(), verificationId);
  42. return;
  43. }
  44. try {
  45. extras.putString(DirectStatementService.EXTRA_TARGET_ASSET_DESCRIPTOR,
  46. createAndroidAssetString(context, packageName));
  47. } catch (NameNotFoundException e) {
  48. Log.w(TAG, "Error when processing input Android package: " + e.getMessage());
  49. sendErrorToPackageManager(context.getPackageManager(), verificationId);
  50. return;
  51. }
  52. extras.putParcelable(DirectStatementService.EXTRA_RESULT_RECEIVER,
  53. new IsAssociatedResultReceiver(
  54. new Handler(), context.getPackageManager(), verificationId));
  55. // Required for CTS: log a few details of the validcation operation to be performed
  56. logValidationParametersForCTS(verificationId, scheme, finalHosts, packageName);
  57. serviceIntent.putExtras(extras);
  58. context.startService(serviceIntent);//开启服务,找到服务类
  59. }
  60. } else {
  61. Log.w(TAG, "Intent action not supported: " + action);
  62. }
  63. }

通过start一个DirectStatementService进行验证

8.看一下DirectStatementService类

  1. mHandler.post(new ExceptionLoggingFutureTask<Void>(
  2. new IsAssociatedCallable(sources, target, relation, resultReceiver), TAG));

看一下这个位子,使用的地方

9、那么到IsAssociatedCallable类,verifyOneSource方法

  1. private boolean verifyOneSource(AbstractAsset source, AbstractAssetMatcher target,
  2. Relation relation) throws AssociationServiceException {
  3. Result statements = mStatementRetriever.retrieveStatements(source);
  4. for (Statement statement : statements.getStatements()) {
  5. if (relation.matches(statement.getRelation())
  6. && target.matches(statement.getTarget())) {
  7. return true;
  8. }
  9. }
  10. return false;
  11. }

看一下retrieveStatements方法

10.DirectStatementRetriever类里面的retrieveStatements方法:

  1. @Override
  2. public Result retrieveStatements(AbstractAsset source) throws AssociationServiceException {
  3. if (source instanceof AndroidAppAsset) {
  4. return retrieveFromAndroid((AndroidAppAsset) source);
  5. } else if (source instanceof WebAsset) {
  6. return retrieveFromWeb((WebAsset) source);//重点,直接看这个方法
  7. } else {
  8. throw new AssociationServiceException("Namespace is not supported.");
  9. }
  10. }

11.最终到这个retrieveStatementFromUrl方法体里面:

  1. //其中URL配置多一个/.well-known/assetlinks.json;最终看到了吧
  2. private Result retrieveStatementFromUrl(String urlString, int maxIncludeLevel,
  3. AbstractAsset source)
  4. throws AssociationServiceException {
  5. List<Statement> statements = new ArrayList<Statement>();
  6. if (maxIncludeLevel < 0) {
  7. return Result.create(statements, DO_NOT_CACHE_RESULT);
  8. }
  9. WebContent webContent;
  10. try {
  11. URL url = new URL(urlString);
  12. if (!source.followInsecureInclude()
  13. && !url.getProtocol().toLowerCase().equals("https")) {
  14. return Result.create(statements, DO_NOT_CACHE_RESULT);
  15. }
  16. webContent = mUrlFetcher.getWebContentFromUrlWithRetry(url,
  17. HTTP_CONTENT_SIZE_LIMIT_IN_BYTES, HTTP_CONNECTION_TIMEOUT_MILLIS,
  18. HTTP_CONNECTION_BACKOFF_MILLIS, HTTP_CONNECTION_RETRY);//通过网络请求获取配置
  19. } catch (IOException | InterruptedException e) {
  20. return Result.create(statements, DO_NOT_CACHE_RESULT);
  21. }
  22. try {
  23. ParsedStatement result = StatementParser
  24. .parseStatementList(webContent.getContent(), source);
  25. statements.addAll(result.getStatements());
  26. for (String delegate : result.getDelegates()) {
  27. statements.addAll(
  28. retrieveStatementFromUrl(delegate, maxIncludeLevel - 1, source)
  29. .getStatements());
  30. }
  31. return Result.create(statements, webContent.getExpireTimeMillis());
  32. } catch (JSONException | IOException e) {
  33. return Result.create(statements, DO_NOT_CACHE_RESULT);
  34. }
  35. }

12.UrlFetcher获取服务端配置,然后发给之前的receiver进行验证

  1. public WebContent getWebContentFromUrl(URL url, long fileSizeLimit, int connectionTimeoutMillis)
  2. throws AssociationServiceException, IOException {
  3. final String scheme = url.getProtocol().toLowerCase(Locale.US);
  4. if (!scheme.equals("http") && !scheme.equals("https")) {
  5. throw new IllegalArgumentException("The url protocol should be on http or https.");
  6. }
  7. HttpURLConnection connection = null;
  8. try {
  9. connection = (HttpURLConnection) url.openConnection();
  10. connection.setInstanceFollowRedirects(true);
  11. connection.setConnectTimeout(connectionTimeoutMillis);
  12. connection.setReadTimeout(connectionTimeoutMillis);
  13. connection.setUseCaches(true);
  14. connection.setInstanceFollowRedirects(false);
  15. connection.addRequestProperty("Cache-Control", "max-stale=60");
  16. if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
  17. Log.e(TAG, "The responses code is not 200 but " + connection.getResponseCode());
  18. return new WebContent("", DO_NOT_CACHE_RESULT);
  19. }
  20. if (connection.getContentLength() > fileSizeLimit) {
  21. Log.e(TAG, "The content size of the url is larger than " + fileSizeLimit);
  22. return new WebContent("", DO_NOT_CACHE_RESULT);
  23. }
  24. Long expireTimeMillis = getExpirationTimeMillisFromHTTPHeader(
  25. connection.getHeaderFields());
  26. return new WebContent(inputStreamToString(
  27. connection.getInputStream(), connection.getContentLength(), fileSizeLimit),
  28. expireTimeMillis);
  29. } finally {
  30. if (connection != null) {
  31. connection.disconnect();
  32. }
  33. }
  34. }

最终还是走了网络请求了,在安装的过程中还是验证;所以appLink只有链接网络,和翻墙的时候才会进行验证;而且仅仅一次,因为会记录到本地去;

验证流程图:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/296708
推荐阅读
相关标签
  

闽ICP备14008679号