am 4eb03f8f: Merge "Remove obsolete code for App Links IntentFilterVerifier" into mnc-dev

* commit '4eb03f8fd28e757bd636a899c8679731c7eed0a9':
  Remove obsolete code for App Links IntentFilterVerifier
This commit is contained in:
Fabrice Di Meglio
2015-05-01 21:37:12 +00:00
committed by Android Git Automerger
8 changed files with 0 additions and 726 deletions

View File

@@ -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))

View File

@@ -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>

View File

@@ -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
# ******************************************************************

View File

@@ -1 +0,0 @@
-verbose

View File

@@ -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>

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}