赞
踩
最近在做AppLink相关的,但是在所有配置搭建完毕之后,发现部分的手机无法实现AppLink的功能。比如华为,三星等等国外的手机;国内的oppo,vivo,小米等是可以的;
那么是为什么呢?那么就从源码开始观察一下呗;想一想,先从PackageManagerService开始看。为什么呢?看名称就和包相关。其实另一方面,这边做过一个测试,当翻墙安装APP的一次之后,Applink每次都成功了(三星和华为)。那么我们多关注一下PackageManagerService这一块了。
一、PackageManagerService
1.installPackageLI方法里面,一路看下去直到startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg); 看名称,就是做验证,
2.看一下 startIntentFilterVerifications方法,里面跟随的 // 这个很重要
- private void startIntentFilterVerifications(int userId, boolean replacing,
- PackageParser.Package pkg) {
- if (mIntentFilterVerifierComponent == null) {
- Slog.w(TAG, "No IntentFilter verification will not be done as "
- + "there is no IntentFilterVerifier available!");
- return;
- }
-
- final int verifierUid = getPackageUid(
- mIntentFilterVerifierComponent.getPackageName(),
- MATCH_DEBUG_TRIAGED_MISSING,
- (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
-
- Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);//handler
- msg.obj = new IFVerificationParams(pkg, replacing, userId, verifierUid);//创建了认证的东西
- mHandler.sendMessage(msg);
-
- final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
- for (int i = 0; i < childCount; i++) {
- PackageParser.Package childPkg = pkg.childPackages.get(i);
- msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
- msg.obj = new IFVerificationParams(childPkg, replacing, userId, verifierUid);
- mHandler.sendMessage(msg);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
根据于START_INTENT_FILTER_VERIFICATIONS去查找一下
3.startIntentFilterVerifications发送一个消息,随后调用verifyIntentFiltersIfNeeded进行验证
代码如下:
- case START_INTENT_FILTER_VERIFICATIONS: {
- IFVerificationParams params = (IFVerificationParams) msg.obj;
- verifyIntentFiltersIfNeeded(params.userId, params.verifierUid,
- params.replacing, params.pkg);//这个一块又是一个重要的点,跟随下去
- break;
- }
4.看一下verifyIntentFiltersIfNeeded方法:主要做了几个点:检查、搜集、验证。在检查阶段,首先判断是否有设置http/https scheme的Activity,并且是否满足设置了Intent.ACTION_DEFAULT与Intent.ACTION_VIEW,如果没有,则无需验证
代码如下:
- private void verifyIntentFiltersIfNeeded(int userId, int verifierUid, boolean replacing,
- PackageParser.Package pkg) {
- int size = pkg.activities.size();
- if (size == 0) {
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "No activity, so no need to verify any IntentFilter!");
- return;
- }
-
- final boolean hasDomainURLs = hasDomainURLs(pkg);//判断这个是否含有Applink的url,以及配置
- if (!hasDomainURLs) {
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "No domain URLs, so no need to verify any IntentFilter!");
- return;
- }
-
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Checking for userId:" + userId
- + " if any IntentFilter from the " + size
- + " Activities needs verification ...");
-
- int count = 0;
- final String packageName = pkg.packageName;
-
- synchronized (mPackages) {
- // If this is a new install and we see that we've already run verification for this
- // package, we have nothing to do: it means the state was restored from backup.
- if (!replacing) {
- IntentFilterVerificationInfo ivi =
- mSettings.getIntentFilterVerificationLPr(packageName);
- if (ivi != null) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.i(TAG, "Package " + packageName+ " already verified: status="
- + ivi.getStatusString());
- }
- return;
- }
- }
-
- // If any filters need to be verified, then all need to be.
- boolean needToVerify = false;//是否验证
- for (PackageParser.Activity a : pkg.activities) {
- for (ActivityIntentInfo filter : a.intents) {
- if (filter.needsVerification()//是否设置了autoverify
- && needsNetworkVerificationLPr(filter)) {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG,
- "Intent filter needs verification, so processing all filters");
- }
- needToVerify = true;
- break;
- }
- }
- }
-
- if (needToVerify) {//收集
- final int verificationId = mIntentFilterVerificationToken++;
- for (PackageParser.Activity a : pkg.activities) {
- for (ActivityIntentInfo filter : a.intents) {
- if (filter.handlesWebUris(true) && needsNetworkVerificationLPr(filter)) {
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "Verification needed for IntentFilter:" + filter.toString());
- mIntentFilterVerifier.addOneIntentFilterVerification(
- verifierUid, userId, verificationId, filter, packageName);
- count++;
- }
- }
- }
- }
- }
-
- if (count > 0) {
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG, "Starting " + count
- + " IntentFilter verification" + (count > 1 ? "s" : "")
- + " for userId:" + userId);
- mIntentFilterVerifier.startVerifications(userId);//
- } else {
- if (DEBUG_DOMAIN_VERIFICATION) {
- Slog.d(TAG, "No filters or not all autoVerify for " + packageName);
- }
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
5.startVerifications这个方法继续下去:内部类IntentVerifierProxy的sendVerificationRequest
- @Override
- public void startVerifications(int userId) {
- // Launch verifications requests
- int count = mCurrentIntentFilterVerifications.size();
- for (int n=0; n<count; n++) {
- int verificationId = mCurrentIntentFilterVerifications.get(n);
- final IntentFilterVerificationState ivs =
- mIntentFilterVerificationStates.get(verificationId);
-
- String packageName = ivs.getPackageName();
-
- ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
- final int filterCount = filters.size();
- ArraySet<String> domainsSet = new ArraySet<>();
- for (int m=0; m<filterCount; m++) {
- PackageParser.ActivityIntentInfo filter = filters.get(m);
- domainsSet.addAll(filter.getHostsList());
- }
- synchronized (mPackages) {
- if (mSettings.createIntentFilterVerificationIfNeededLPw(
- packageName, domainsSet) != null) {
- scheduleWriteSettingsLocked();
- }
- }
- sendVerificationRequest(verificationId, ivs);//关键
- }
- mCurrentIntentFilterVerifications.clear();
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
6.看一下sendVerificationRequest方法代码:
- private void sendVerificationRequest(int verificationId, IntentFilterVerificationState ivs) {
- Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);//action,广播
- verificationIntent.putExtra(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
- verificationId);
- verificationIntent.putExtra(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
- getDefaultScheme());
- verificationIntent.putExtra(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
- ivs.getHostsString());
- verificationIntent.putExtra(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
- ivs.getPackageName());
- verificationIntent.setComponent(mIntentFilterVerifierComponent);
- verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-
- DeviceIdleController.LocalService idleController = getDeviceIdleController();
- idleController.addPowerSaveTempWhitelistApp(Process.myUid(),
- mIntentFilterVerifierComponent.getPackageName(), getVerificationTimeout(),
- UserHandle.USER_SYSTEM, true, "intent filter verifier");
-
- mContext.sendBroadcastAsUser(verificationIntent, UserHandle.SYSTEM);//发送
- if (DEBUG_DOMAIN_VERIFICATION) Slog.d(TAG,
- "Sending IntentFilter verification broadcast");
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
7.看一下广播发送的地方IntentFilterVerificationReceiver类里面onReceive方法:
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION.equals(action)) {
- Bundle inputExtras = intent.getExtras();
- if (inputExtras != null) {
- Intent serviceIntent = new Intent(context, DirectStatementService.class);//服务类
- serviceIntent.setAction(DirectStatementService.CHECK_ALL_ACTION);
-
- int verificationId = inputExtras.getInt(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID);
- String scheme = inputExtras.getString(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME);
- String hosts = inputExtras.getString(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS);
- String packageName = inputExtras.getString(
- PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME);
-
- Bundle extras = new Bundle();
- extras.putString(DirectStatementService.EXTRA_RELATION, HANDLE_ALL_URLS_RELATION);
-
- String[] hostList = hosts.split(" ");
- if (hostList.length > MAX_HOSTS_PER_REQUEST) {
- Log.w(TAG, String.format(TOO_MANY_HOSTS_FORMAT,
- hostList.length, MAX_HOSTS_PER_REQUEST));
- sendErrorToPackageManager(context.getPackageManager(), verificationId);
- return;
- }
-
- ArrayList<String> finalHosts = new ArrayList<String>(hostList.length);
- try {
- ArrayList<String> sourceAssets = new ArrayList<String>();
- for (String host : hostList) {
- // "*.example.tld" is validated via https://example.tld
- if (host.startsWith("*.")) {
- host = host.substring(2);
- }
- sourceAssets.add(createWebAssetString(scheme, host));
- finalHosts.add(host);
- }
- extras.putStringArrayList(DirectStatementService.EXTRA_SOURCE_ASSET_DESCRIPTORS,
- sourceAssets);
- } catch (MalformedURLException e) {
- Log.w(TAG, "Error when processing input host: " + e.getMessage());
- sendErrorToPackageManager(context.getPackageManager(), verificationId);
- return;
- }
- try {
- extras.putString(DirectStatementService.EXTRA_TARGET_ASSET_DESCRIPTOR,
- createAndroidAssetString(context, packageName));
- } catch (NameNotFoundException e) {
- Log.w(TAG, "Error when processing input Android package: " + e.getMessage());
- sendErrorToPackageManager(context.getPackageManager(), verificationId);
- return;
- }
- extras.putParcelable(DirectStatementService.EXTRA_RESULT_RECEIVER,
- new IsAssociatedResultReceiver(
- new Handler(), context.getPackageManager(), verificationId));
-
- // Required for CTS: log a few details of the validcation operation to be performed
- logValidationParametersForCTS(verificationId, scheme, finalHosts, packageName);
-
- serviceIntent.putExtras(extras);
- context.startService(serviceIntent);//开启服务,找到服务类
- }
- } else {
- Log.w(TAG, "Intent action not supported: " + action);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
通过start一个DirectStatementService进行验证
8.看一下DirectStatementService类
- mHandler.post(new ExceptionLoggingFutureTask<Void>(
- new IsAssociatedCallable(sources, target, relation, resultReceiver), TAG));
看一下这个位子,使用的地方
9、那么到IsAssociatedCallable类,verifyOneSource方法
- private boolean verifyOneSource(AbstractAsset source, AbstractAssetMatcher target,
- Relation relation) throws AssociationServiceException {
- Result statements = mStatementRetriever.retrieveStatements(source);
- for (Statement statement : statements.getStatements()) {
- if (relation.matches(statement.getRelation())
- && target.matches(statement.getTarget())) {
- return true;
- }
- }
- return false;
- }
-
看一下retrieveStatements方法
10.DirectStatementRetriever类里面的retrieveStatements方法:
- @Override
- public Result retrieveStatements(AbstractAsset source) throws AssociationServiceException {
- if (source instanceof AndroidAppAsset) {
- return retrieveFromAndroid((AndroidAppAsset) source);
- } else if (source instanceof WebAsset) {
- return retrieveFromWeb((WebAsset) source);//重点,直接看这个方法
- } else {
- throw new AssociationServiceException("Namespace is not supported.");
- }
- }
11.最终到这个retrieveStatementFromUrl方法体里面:
- //其中URL配置多一个/.well-known/assetlinks.json;最终看到了吧
- private Result retrieveStatementFromUrl(String urlString, int maxIncludeLevel,
- AbstractAsset source)
- throws AssociationServiceException {
- List<Statement> statements = new ArrayList<Statement>();
- if (maxIncludeLevel < 0) {
- return Result.create(statements, DO_NOT_CACHE_RESULT);
- }
-
- WebContent webContent;
- try {
- URL url = new URL(urlString);
- if (!source.followInsecureInclude()
- && !url.getProtocol().toLowerCase().equals("https")) {
- return Result.create(statements, DO_NOT_CACHE_RESULT);
- }
- webContent = mUrlFetcher.getWebContentFromUrlWithRetry(url,
- HTTP_CONTENT_SIZE_LIMIT_IN_BYTES, HTTP_CONNECTION_TIMEOUT_MILLIS,
- HTTP_CONNECTION_BACKOFF_MILLIS, HTTP_CONNECTION_RETRY);//通过网络请求获取配置
- } catch (IOException | InterruptedException e) {
- return Result.create(statements, DO_NOT_CACHE_RESULT);
- }
-
- try {
- ParsedStatement result = StatementParser
- .parseStatementList(webContent.getContent(), source);
- statements.addAll(result.getStatements());
- for (String delegate : result.getDelegates()) {
- statements.addAll(
- retrieveStatementFromUrl(delegate, maxIncludeLevel - 1, source)
- .getStatements());
- }
- return Result.create(statements, webContent.getExpireTimeMillis());
- } catch (JSONException | IOException e) {
- return Result.create(statements, DO_NOT_CACHE_RESULT);
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
12.UrlFetcher获取服务端配置,然后发给之前的receiver进行验证
- public WebContent getWebContentFromUrl(URL url, long fileSizeLimit, int connectionTimeoutMillis)
- throws AssociationServiceException, IOException {
- final String scheme = url.getProtocol().toLowerCase(Locale.US);
- if (!scheme.equals("http") && !scheme.equals("https")) {
- throw new IllegalArgumentException("The url protocol should be on http or https.");
- }
-
- HttpURLConnection connection = null;
- try {
- connection = (HttpURLConnection) url.openConnection();
- connection.setInstanceFollowRedirects(true);
- connection.setConnectTimeout(connectionTimeoutMillis);
- connection.setReadTimeout(connectionTimeoutMillis);
- connection.setUseCaches(true);
- connection.setInstanceFollowRedirects(false);
- connection.addRequestProperty("Cache-Control", "max-stale=60");
-
- if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
- Log.e(TAG, "The responses code is not 200 but " + connection.getResponseCode());
- return new WebContent("", DO_NOT_CACHE_RESULT);
- }
-
- if (connection.getContentLength() > fileSizeLimit) {
- Log.e(TAG, "The content size of the url is larger than " + fileSizeLimit);
- return new WebContent("", DO_NOT_CACHE_RESULT);
- }
-
- Long expireTimeMillis = getExpirationTimeMillisFromHTTPHeader(
- connection.getHeaderFields());
-
- return new WebContent(inputStreamToString(
- connection.getInputStream(), connection.getContentLength(), fileSizeLimit),
- expireTimeMillis);
- } finally {
- if (connection != null) {
- connection.disconnect();
- }
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
最终还是走了网络请求了,在安装的过程中还是验证;所以appLink只有链接网络,和翻墙的时候才会进行验证;而且仅仅一次,因为会记录到本地去;
验证流程图:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。