/*
* 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.talkback.labeling;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.text.TextUtils;
import android.util.Log;
import com.android.utils.LogUtils;
import com.android.utils.StringBuilderUtils;
import com.android.utils.labeling.Label;
import com.android.utils.labeling.LabelProviderClient;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class DataConsistencyCheckRequest extends LabelClientRequest<List<Label>> {
private Context mContext;
private OnDataConsistencyCheckCallback mCallback;
public DataConsistencyCheckRequest(LabelProviderClient client, Context context,
OnDataConsistencyCheckCallback callback) {
super(client);
mContext = context;
mCallback = callback;
}
@Override
public List<Label> doInBackground() {
final List<Label> allLabels = mClient.getCurrentLabels();
if ((allLabels == null) || allLabels.isEmpty()) {
return null;
}
final PackageManager pm = mContext.getPackageManager();
final List<Label> candidates = new ArrayList<>(allLabels);
ListIterator<Label> i = candidates.listIterator();
// Iterate through the labels database, and prune labels that belong
// to valid packages.
while (i.hasNext()) {
final Label l = i.next();
// Ensure the label has a matching installed package.
final String packageName = l.getPackageName();
PackageInfo packageInfo;
try {
packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
} catch (PackageManager.NameNotFoundException e) {
// If there's no installed package, leave the label in the
// list for removal.
LogUtils.log(DataConsistencyCheckRequest.class, Log.VERBOSE,
"Consistency check removing label for unknown package %s.",
packageName);
continue;
}
// Ensure the signature hash of the application matches
// the hash of the package when the label was stored.
final String expectedHash = l.getPackageSignature();
final String actualHash = computePackageSignatureHash(packageInfo);
if (TextUtils.isEmpty(expectedHash) || TextUtils.isEmpty(actualHash)
|| !expectedHash.equals(actualHash)) {
// If the expected or actual signature hashes aren't
// valid, or they don't match, leave the label in the list
// for removal.
LogUtils.log(DataConsistencyCheckRequest.class, Log.WARN,
"Consistency check removing label due to signature mismatch " +
"for package %s.",
packageName);
continue;
}
// If the label has passed all consistency checks, prune the
// label from the list of potentials for removal.
i.remove();
}
return candidates; // now containing only labels for removal
}
@Override
public void onPostExecute(List<Label> labelsToRemove) {
if (mCallback != null) {
mCallback.onConsistencyCheckCompleted(labelsToRemove);
}
}
private static String computePackageSignatureHash(PackageInfo packageInfo) {
String signatureHash = "";
final Signature[] sigs = packageInfo.signatures;
try {
final MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
for (Signature s : sigs) {
messageDigest.update(s.toByteArray());
}
signatureHash = StringBuilderUtils.bytesToHexString(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
LogUtils.log(DataConsistencyCheckRequest.class, Log.WARN,
"Unable to create SHA-1 MessageDigest");
}
return signatureHash;
}
public interface OnDataConsistencyCheckCallback {
public void onConsistencyCheckCompleted(List<Label> labelsToRemove);
}
}