am 6ae03f34: am 4eb03f8f: Merge "Remove obsolete code for App Links IntentFilterVerifier" into mnc-dev
* commit '6ae03f34d5f8d5dd10f47c62ee291e9fc149809e': Remove obsolete code for App Links IntentFilterVerifier
This commit is contained in:
@@ -1,22 +0,0 @@
|
|||||||
LOCAL_PATH:= $(call my-dir)
|
|
||||||
|
|
||||||
# Build the IntentFilterVerifier.
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
|
||||||
volley \
|
|
||||||
|
|
||||||
LOCAL_JAVA_LIBRARIES += org.apache.http.legacy
|
|
||||||
|
|
||||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
|
||||||
|
|
||||||
LOCAL_PACKAGE_NAME := IntentFilterVerifier
|
|
||||||
|
|
||||||
LOCAL_PRIVILEGED_MODULE := true
|
|
||||||
|
|
||||||
LOCAL_PROGUARD_FLAGS := $(proguard.flags)
|
|
||||||
|
|
||||||
include $(BUILD_PACKAGE)
|
|
||||||
|
|
||||||
# Build the test package.
|
|
||||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2015 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
package="com.android.verifier.intentfilter"
|
|
||||||
coreApp="true">
|
|
||||||
<uses-permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
|
||||||
|
|
||||||
<application
|
|
||||||
android:label="@string/service_name"
|
|
||||||
android:allowBackup="false">
|
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name="com.android.verifier.intentfilter.IntentVerificationReceiver"
|
|
||||||
android:permission="android.permission.BIND_INTENT_FILTER_VERIFIER" >
|
|
||||||
<intent-filter
|
|
||||||
android:priority="-1" >
|
|
||||||
<action android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
|
|
||||||
<data android:mimeType="application/vnd.android.package-archive" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<service android:name=".IntentVerificationService"
|
|
||||||
android:label="@string/service_name"
|
|
||||||
android:exported="false"/>
|
|
||||||
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
# Copyright (C) 2015 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
# If you don't need to do a full clean build but would like to touch
|
|
||||||
# a file or delete some intermediate files, add a clean step to the end
|
|
||||||
# of the list. These steps will only be run once, if they haven't been
|
|
||||||
# run before.
|
|
||||||
#
|
|
||||||
# E.g.:
|
|
||||||
# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
|
|
||||||
# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
|
|
||||||
#
|
|
||||||
# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
|
|
||||||
# files that are missing or have been moved.
|
|
||||||
#
|
|
||||||
# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
|
|
||||||
# Use $(OUT_DIR) to refer to the "out" directory.
|
|
||||||
#
|
|
||||||
# If you need to re-do something that's already mentioned, just copy
|
|
||||||
# the command and add it to the bottom of the list. E.g., if a change
|
|
||||||
# that you made last week required touching a file and a change you
|
|
||||||
# made today requires touching the same file, just copy the old
|
|
||||||
# touch step and add it to the end of the list.
|
|
||||||
#
|
|
||||||
# *****************************************************************
|
|
||||||
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
|
|
||||||
# *****************************************************************
|
|
||||||
|
|
||||||
# For example:
|
|
||||||
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
|
|
||||||
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
|
|
||||||
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
|
|
||||||
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
|
|
||||||
|
|
||||||
# ******************************************************************
|
|
||||||
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
|
|
||||||
# ******************************************************************
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
-verbose
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Copyright (C) 2015 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.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
|
||||||
<!-- Package name shown to users when they look at installed applications
|
|
||||||
and running processes. This service verifies packages that are
|
|
||||||
requested to be installed. [CHAR LIMIT=50] -->
|
|
||||||
<string name="service_name">Basic Intent Filter Verification Service</string>
|
|
||||||
|
|
||||||
</resources>
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 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.android.verifier.intentfilter;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.net.ConnectivityManager;
|
|
||||||
import android.net.NetworkInfo;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Slog;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class IntentVerificationReceiver extends BroadcastReceiver {
|
|
||||||
static final String TAG = IntentVerificationReceiver.class.getName();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
final String action = intent.getAction();
|
|
||||||
if (Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION.equals(action)) {
|
|
||||||
Bundle extras = intent.getExtras();
|
|
||||||
if (extras != null) {
|
|
||||||
int verificationId = extras.getInt(
|
|
||||||
PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID);
|
|
||||||
String hosts = extras.getString(
|
|
||||||
PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS);
|
|
||||||
|
|
||||||
Log.d(TAG, "Received IntentFilter verification broadcast with verificationId: "
|
|
||||||
+ verificationId);
|
|
||||||
|
|
||||||
if (canDoVerification(context)) {
|
|
||||||
Intent serviceIntent = new Intent(context, IntentVerificationService.class);
|
|
||||||
serviceIntent.fillIn(intent, 0);
|
|
||||||
serviceIntent.putExtras(intent.getExtras());
|
|
||||||
|
|
||||||
Slog.d(TAG, "Starting Intent Verification Service.");
|
|
||||||
|
|
||||||
context.startService(serviceIntent);
|
|
||||||
} else {
|
|
||||||
sendVerificationFailure(context, verificationId, hosts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Unexpected action: " + action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendVerificationFailure(Context context, int verificationId, String hosts) {
|
|
||||||
List<String> list = Arrays.asList(hosts.split(" "));
|
|
||||||
context.getPackageManager().verifyIntentFilter(
|
|
||||||
verificationId, PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, list);
|
|
||||||
|
|
||||||
Log.d(TAG, "No network! Failing IntentFilter verification with verificationId: " +
|
|
||||||
verificationId + " and hosts: " + hosts);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canDoVerification(Context context) {
|
|
||||||
return hasNetwork(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNetwork(Context context) {
|
|
||||||
ConnectivityManager cm =
|
|
||||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
||||||
if (cm != null) {
|
|
||||||
NetworkInfo info = cm.getActiveNetworkInfo();
|
|
||||||
return (info != null) && info.isConnected();
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 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.android.verifier.intentfilter;
|
|
||||||
|
|
||||||
import com.android.volley.Response;
|
|
||||||
import com.android.volley.toolbox.JsonArrayRequest;
|
|
||||||
import org.json.JSONArray;
|
|
||||||
|
|
||||||
public class IntentVerificationRequest extends JsonArrayRequest {
|
|
||||||
|
|
||||||
public IntentVerificationRequest(String url, Response.Listener<JSONArray> listener,
|
|
||||||
Response.ErrorListener errorListener) {
|
|
||||||
super(url, listener, errorListener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,468 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 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.android.verifier.intentfilter;
|
|
||||||
|
|
||||||
import android.app.Service;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.Signature;
|
|
||||||
import android.net.ConnectivityManager;
|
|
||||||
import android.net.NetworkInfo;
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.HandlerThread;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Slog;
|
|
||||||
import com.android.volley.RequestQueue;
|
|
||||||
import com.android.volley.Response;
|
|
||||||
import com.android.volley.VolleyError;
|
|
||||||
import com.android.volley.toolbox.Volley;
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class IntentVerificationService extends Service {
|
|
||||||
private static final String TAG = "IntentVerificationService";
|
|
||||||
|
|
||||||
private static final String WELL_KNOWN_ASSOCIATIONS_JSON = "/.well-known/associations.json";
|
|
||||||
private static final String DEFAULT_SCHEME = "https";
|
|
||||||
|
|
||||||
private static final String JSON_KEY_TARGET = "target";
|
|
||||||
private static final String JSON_KEY_NAMESPACE = "namespace";
|
|
||||||
private static final String JSON_KEY_PACKAGE_NAME = "package_name";
|
|
||||||
private static final String JSON_KEY_CERT_FINGERPRINTS = "sha256_cert_fingerprints";
|
|
||||||
|
|
||||||
private static final String JSON_VAL_ANDROID_APP = "android_app";
|
|
||||||
|
|
||||||
private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
||||||
'A', 'B', 'C', 'D', 'E', 'F' };
|
|
||||||
|
|
||||||
private ConnectivityManager mConnectivityManager;
|
|
||||||
private Looper mHandlerLooper;
|
|
||||||
private VerificationHandler mHandler;
|
|
||||||
private RequestQueue mRequestQueue;
|
|
||||||
|
|
||||||
private static class VerificationState {
|
|
||||||
public final int verificationId;
|
|
||||||
public final String hosts;
|
|
||||||
public final String packageName;
|
|
||||||
public final Set<String> fingerprints;
|
|
||||||
public int responseCode = PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS;
|
|
||||||
public int counter;
|
|
||||||
public int numberOfHosts;
|
|
||||||
public ArrayList<String> failedHosts = new ArrayList<>();
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
public VerificationState(int id, String h, String p, Set<String> fps) {
|
|
||||||
verificationId = id;
|
|
||||||
hosts = h;
|
|
||||||
packageName = p;
|
|
||||||
fingerprints = fps;
|
|
||||||
numberOfHosts = hosts.split(" ").length;
|
|
||||||
}
|
|
||||||
public boolean setResponseCodeAndCheckMax(int code) {
|
|
||||||
synchronized (lock) {
|
|
||||||
if (code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
|
|
||||||
responseCode = code;
|
|
||||||
counter++;
|
|
||||||
} else if (code == PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS) {
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
return (counter == numberOfHosts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addFailedHost(String host) {
|
|
||||||
synchronized (failedHosts) {
|
|
||||||
failedHosts.add(host);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<String> getFailedHosts() {
|
|
||||||
return failedHosts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private HashMap<Integer, VerificationState> mVerificationMap =
|
|
||||||
new HashMap<Integer, VerificationState>();
|
|
||||||
|
|
||||||
private class VerificationHandler extends Handler {
|
|
||||||
private static final int MSG_STOP_SERVICE = 0;
|
|
||||||
private static final int MSG_VERIFY_INTENT_START = 1;
|
|
||||||
private static final int MSG_VERIFY_INTENT_DONE = 2;
|
|
||||||
|
|
||||||
private static final long SHUTDOWN_DELAY_MILLIS = 8 * 1000;
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
|
|
||||||
public VerificationHandler(Context context, Looper looper) {
|
|
||||||
super(looper);
|
|
||||||
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case MSG_VERIFY_INTENT_START:
|
|
||||||
final Intent intent = (Intent) msg.obj;
|
|
||||||
Bundle extras = intent.getExtras();
|
|
||||||
boolean immediate = false;
|
|
||||||
|
|
||||||
if (extras != null) {
|
|
||||||
immediate = doVerification(extras);
|
|
||||||
}
|
|
||||||
|
|
||||||
// There was no network, so we can stop soon
|
|
||||||
if (immediate) {
|
|
||||||
stopDelayed();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MSG_VERIFY_INTENT_DONE:
|
|
||||||
VerificationState vs = (VerificationState) msg.obj;
|
|
||||||
processVerificationDone(mContext, vs);
|
|
||||||
clearVerificationState(vs);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MSG_STOP_SERVICE:
|
|
||||||
stopSelf();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Slog.i(TAG, "Unknown message posted " + msg.toString());
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopDelayed() {
|
|
||||||
removeMessages(MSG_STOP_SERVICE);
|
|
||||||
sendEmptyMessageDelayed(MSG_STOP_SERVICE, SHUTDOWN_DELAY_MILLIS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private VerificationState getVerificationState(int id, String hosts, String packageName,
|
|
||||||
Set<String> fingerprints) {
|
|
||||||
synchronized (mVerificationMap) {
|
|
||||||
VerificationState vs = mVerificationMap.get(id);
|
|
||||||
if (vs == null) {
|
|
||||||
vs = new VerificationState(id, hosts, packageName, fingerprints);
|
|
||||||
}
|
|
||||||
return vs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearVerificationState(VerificationState vs) {
|
|
||||||
mVerificationMap.remove(vs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean doVerification(Bundle extras) {
|
|
||||||
String scheme = extras.getString(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME);
|
|
||||||
if (TextUtils.isEmpty(scheme)) {
|
|
||||||
scheme = DEFAULT_SCHEME;
|
|
||||||
}
|
|
||||||
|
|
||||||
int verificationId = extras.getInt(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID);
|
|
||||||
String hosts = extras.getString(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS);
|
|
||||||
String packageName = extras.getString(
|
|
||||||
PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME);
|
|
||||||
|
|
||||||
Set<String> fingerprints = getFingerprints(packageName);
|
|
||||||
|
|
||||||
Log.d(TAG, "Received IntentFilter verification broadcast with verificationId:" +
|
|
||||||
verificationId + " hosts:'" + hosts + "' scheme:" + scheme);
|
|
||||||
|
|
||||||
VerificationState vs = getVerificationState(verificationId, hosts, packageName,
|
|
||||||
fingerprints);
|
|
||||||
|
|
||||||
if (hasNetwork()) {
|
|
||||||
sendNetworkVerifications(scheme, vs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No network, so fail immediately
|
|
||||||
sendFailureResponseIfNeeded(vs);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> getFingerprints(String packageName) {
|
|
||||||
Context context = getApplicationContext();
|
|
||||||
try {
|
|
||||||
Signature[] signatures = context.getPackageManager().getPackageInfo(packageName,
|
|
||||||
PackageManager.GET_SIGNATURES).signatures;
|
|
||||||
if (signatures.length > 0) {
|
|
||||||
HashSet<String> result = new HashSet<String>();
|
|
||||||
for (Signature sig : signatures) {
|
|
||||||
String fingerprint = computeNormalizedSha256Fingerprint(sig.toByteArray());
|
|
||||||
result.add(fingerprint);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
Log.e(TAG, "Cannot get signatures for package name: " + packageName);
|
|
||||||
}
|
|
||||||
return Collections.EMPTY_SET;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String computeNormalizedSha256Fingerprint(byte[] signature) {
|
|
||||||
MessageDigest digester;
|
|
||||||
try {
|
|
||||||
digester = MessageDigest.getInstance("SHA-256");
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new AssertionError("No SHA-256 implementation found.");
|
|
||||||
}
|
|
||||||
digester.update(signature);
|
|
||||||
return byteArrayToHexString(digester.digest());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String byteArrayToHexString(byte[] array) {
|
|
||||||
if (array.length == 0) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
char[] buf = new char[array.length * 3 - 1];
|
|
||||||
|
|
||||||
int bufIndex = 0;
|
|
||||||
for (int i = 0; i < array.length; i++) {
|
|
||||||
byte b = array[i];
|
|
||||||
if (i > 0) {
|
|
||||||
buf[bufIndex++] = ':';
|
|
||||||
}
|
|
||||||
buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
|
|
||||||
buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
|
|
||||||
}
|
|
||||||
return new String(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getAssociationPath() {
|
|
||||||
return WELL_KNOWN_ASSOCIATIONS_JSON;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendNetworkVerifications(String scheme, final VerificationState vs) {
|
|
||||||
final int verificationId = vs.verificationId;
|
|
||||||
final String hosts = vs.hosts;
|
|
||||||
|
|
||||||
String[] array = hosts.split(" ");
|
|
||||||
for (final String host : array) {
|
|
||||||
try {
|
|
||||||
final URL url = new URL(scheme, host, getAssociationPath());
|
|
||||||
final String urlStr = url.toString();
|
|
||||||
Log.d(TAG, "Using verification URL: " + urlStr);
|
|
||||||
IntentVerificationRequest req = new IntentVerificationRequest(urlStr,
|
|
||||||
new Response.Listener<JSONArray>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(JSONArray response) {
|
|
||||||
Log.d(TAG, "From: " + urlStr + " received response: "
|
|
||||||
+ response.toString());
|
|
||||||
handleResponse(vs, host, response);
|
|
||||||
}
|
|
||||||
}, new Response.ErrorListener() {
|
|
||||||
@Override
|
|
||||||
public void onErrorResponse(VolleyError error) {
|
|
||||||
Slog.d(TAG, "From: " + urlStr + " got error: " + error.getMessage()
|
|
||||||
+ (error.networkResponse != null ? " with status code: "
|
|
||||||
+ error.networkResponse.statusCode : ""));
|
|
||||||
handleError(vs, host);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
mRequestQueue.add(req);
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
Log.w(TAG, "Cannot send verificationId: " + verificationId + " to host: " + host);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleError(VerificationState vs, String host) {
|
|
||||||
vs.addFailedHost(host);
|
|
||||||
sendFailureResponseIfNeeded(vs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleResponse(VerificationState vs, String host, JSONArray response) {
|
|
||||||
try {
|
|
||||||
if (response.length() == 0) {
|
|
||||||
Log.d(TAG, "Domain response is empty!");
|
|
||||||
handleError(vs, host);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject firstRelation = (JSONObject) response.get(0);
|
|
||||||
if (firstRelation == null) {
|
|
||||||
Log.d(TAG, "Domain response is should have a relation!");
|
|
||||||
handleError(vs, host);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject target = (JSONObject) firstRelation.get(JSON_KEY_TARGET);
|
|
||||||
if (target == null) {
|
|
||||||
Log.d(TAG, "Domain response target is empty!");
|
|
||||||
handleError(vs, host);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String nameSpace = target.getString(JSON_KEY_NAMESPACE);
|
|
||||||
if (TextUtils.isEmpty(nameSpace) || !nameSpace.equals(JSON_VAL_ANDROID_APP)) {
|
|
||||||
Log.d(TAG, "Domain response target name space is not valid: " + nameSpace);
|
|
||||||
handleError(vs, host);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String packageName = target.getString(JSON_KEY_PACKAGE_NAME);
|
|
||||||
JSONArray certFingerprints = target.getJSONArray(JSON_KEY_CERT_FINGERPRINTS);
|
|
||||||
|
|
||||||
// Early exits is the JSON response is not correct for the package name or signature
|
|
||||||
if (TextUtils.isEmpty(packageName)) {
|
|
||||||
Log.d(TAG, "Domain response has empty package name!");
|
|
||||||
handleError(vs, host);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (certFingerprints.length() == 0) {
|
|
||||||
Log.d(TAG, "Domain response has empty cert signature!");
|
|
||||||
handleError(vs, host);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Now do the real test on package name and signature
|
|
||||||
if (!packageName.equalsIgnoreCase(vs.packageName)) {
|
|
||||||
Log.d(TAG, "Domain response has package name mismatch!" + packageName +
|
|
||||||
" vs " + vs.packageName);
|
|
||||||
handleError(vs, host);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final int count = certFingerprints.length();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
String fingerprint = certFingerprints.getString(i);
|
|
||||||
if (!vs.fingerprints.contains(fingerprint)) {
|
|
||||||
Log.d(TAG, "Domain response has cert fingerprint mismatch! " +
|
|
||||||
"The domain fingerprint '" + fingerprint + "' is not from the App");
|
|
||||||
handleError(vs, host);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sendSuccessResponseIfNeeded(vs);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
Log.d(TAG, "Domain response is not well formed", e);
|
|
||||||
handleError(vs, host);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendSuccessResponseIfNeeded(VerificationState vs) {
|
|
||||||
if (vs.setResponseCodeAndCheckMax(PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS)) {
|
|
||||||
sendMessage(vs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendFailureResponseIfNeeded(VerificationState vs) {
|
|
||||||
if (vs.setResponseCodeAndCheckMax(PackageManager.INTENT_FILTER_VERIFICATION_FAILURE)) {
|
|
||||||
sendMessage(vs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendMessage(VerificationState vs) {
|
|
||||||
Message msg = mHandler.obtainMessage(VerificationHandler.MSG_VERIFY_INTENT_DONE);
|
|
||||||
msg.obj = vs;
|
|
||||||
mHandler.sendMessage(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processVerificationDone(Context context, VerificationState state) {
|
|
||||||
int verificationId = state.verificationId;
|
|
||||||
String hosts = state.hosts;
|
|
||||||
int responseCode = state.responseCode;
|
|
||||||
|
|
||||||
final PackageManager pm = context.getPackageManager();
|
|
||||||
|
|
||||||
// Callback the PackageManager
|
|
||||||
pm.verifyIntentFilter(verificationId, responseCode, state.getFailedHosts());
|
|
||||||
Log.d(TAG, "IntentFilter with verificationId: " + verificationId + " and hosts: " +
|
|
||||||
hosts + " got verification code: " + responseCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We only connect to this service from the same process.
|
|
||||||
*/
|
|
||||||
public class LocalBinder extends Binder {
|
|
||||||
IntentVerificationService getService() { return IntentVerificationService.this; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private final IBinder mBinder = new LocalBinder();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBinder onBind(Intent intent) {
|
|
||||||
return mBinder;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
||||||
Slog.i(TAG, "Received start id " + startId + ": " + intent);
|
|
||||||
|
|
||||||
final Message msg = mHandler.obtainMessage(VerificationHandler.MSG_VERIFY_INTENT_START);
|
|
||||||
msg.obj = intent;
|
|
||||||
mHandler.sendMessage(msg);
|
|
||||||
|
|
||||||
return START_STICKY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
|
|
||||||
Slog.d(TAG, "Starting up...");
|
|
||||||
|
|
||||||
final HandlerThread handlerThread = new HandlerThread("IntentVerificationService");
|
|
||||||
handlerThread.start();
|
|
||||||
mHandlerLooper = handlerThread.getLooper();
|
|
||||||
|
|
||||||
mHandler = new VerificationHandler(getApplicationContext(), mHandlerLooper);
|
|
||||||
|
|
||||||
mRequestQueue = Volley.newRequestQueue(this);
|
|
||||||
mRequestQueue.start();
|
|
||||||
|
|
||||||
mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
|
|
||||||
Slog.d(TAG, "Shutting down...");
|
|
||||||
|
|
||||||
mHandlerLooper.quit();
|
|
||||||
mRequestQueue.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasNetwork() {
|
|
||||||
NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
|
|
||||||
return (info != null) && info.isConnected();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user