summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/LauncherApps.java11
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java167
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java87
3 files changed, 262 insertions, 3 deletions
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 983ea9f847cd..766c5660012b 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -136,6 +136,17 @@ public class LauncherApps {
public static final String EXTRA_PIN_ITEM_REQUEST =
"android.content.pm.extra.PIN_ITEM_REQUEST";
+ /**
+ * Metadata key that specifies vouched certs, so any apps signed by a cert in vouched certs
+ * will not show hidden icon in launcher even it does not have a launcher visible activity.
+ *
+ * If an app has this metadata in manifest, it won't be eligible to hide its icon even if its
+ * cert is in vouched certs list.
+ *
+ * @hide
+ */
+ public static final String VOUCHED_CERTS_KEY = "vouched_certs";
+
private final Context mContext;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private final ILauncherApps mService;
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index b6dae1977262..a33f14bab4b1 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -33,6 +33,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ILauncherApps;
import android.content.pm.IOnAppsChangedListener;
+import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -42,6 +43,8 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
import android.graphics.Rect;
import android.net.Uri;
@@ -56,21 +59,31 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
+import android.util.ByteStringUtils;
import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
+import com.android.internal.util.StatLogger;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.wm.ActivityTaskManagerInternal;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Service that manages requests and callbacks for launchers that support
@@ -108,6 +121,17 @@ public class LauncherAppsService extends SystemService {
static class LauncherAppsImpl extends ILauncherApps.Stub {
private static final boolean DEBUG = false;
private static final String TAG = "LauncherAppsService";
+
+ // Stats
+ @VisibleForTesting
+ interface Stats {
+ int INIT_VOUCHED_SIGNATURES = 0;
+ int COUNT = INIT_VOUCHED_SIGNATURES + 1;
+ }
+ private final StatLogger mStatLogger = new StatLogger(new String[] {
+ "initVouchedSignatures"
+ });
+
private final Context mContext;
private final UserManager mUm;
private final UserManagerInternal mUserManagerInternal;
@@ -117,11 +141,16 @@ public class LauncherAppsService extends SystemService {
private final PackageCallbackList<IOnAppsChangedListener> mListeners
= new PackageCallbackList<IOnAppsChangedListener>();
private final DevicePolicyManager mDpm;
+ private final ConcurrentHashMap<UserHandle, Set<String>> mVouchedSignaturesByUser;
+ private final Set<String> mVouchProviders;
private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
+ private final VouchesChangedMonitor mVouchesChangedMonitor = new VouchesChangedMonitor();
private final Handler mCallbackHandler;
+ private final Object mVouchedSignaturesLocked = new Object();
+
public LauncherAppsImpl(Context context) {
mContext = context;
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -136,6 +165,9 @@ public class LauncherAppsService extends SystemService {
mShortcutServiceInternal.addListener(mPackageMonitor);
mCallbackHandler = BackgroundThread.getHandler();
mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mVouchedSignaturesByUser = new ConcurrentHashMap<>();
+ mVouchProviders = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
+ mVouchesChangedMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler);
}
@VisibleForTesting
@@ -355,7 +387,7 @@ public class LauncherAppsService extends SystemService {
}
ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
callingUid, user.getIdentifier());
- if (shouldShowHiddenApp(appInfo)) {
+ if (shouldShowHiddenApp(user, appInfo)) {
ResolveInfo info = getHiddenAppActivityInfo(packageName, callingUid, user);
if (info != null) {
result.add(info);
@@ -371,7 +403,7 @@ public class LauncherAppsService extends SystemService {
user.getIdentifier(), callingUid);
for (ApplicationInfo applicationInfo : installedPackages) {
if (!visiblePackages.contains(applicationInfo.packageName)) {
- if (!shouldShowHiddenApp(applicationInfo)) {
+ if (!shouldShowHiddenApp(user, applicationInfo)) {
continue;
}
ResolveInfo info = getHiddenAppActivityInfo(applicationInfo.packageName,
@@ -387,13 +419,130 @@ public class LauncherAppsService extends SystemService {
}
}
- private static boolean shouldShowHiddenApp(ApplicationInfo appInfo) {
+ private boolean shouldShowHiddenApp(UserHandle user, ApplicationInfo appInfo) {
if (appInfo == null || appInfo.isSystemApp() || appInfo.isUpdatedSystemApp()) {
return false;
}
+ if (!mVouchedSignaturesByUser.containsKey(user)) {
+ initVouchedSignatures(user);
+ }
+ if (mVouchProviders.contains(appInfo.packageName)) {
+ // If it's a vouching packages then we must show hidden app
+ return true;
+ }
+ // If app's signature is in vouch list, do not show hidden app
+ final Set<String> vouches = mVouchedSignaturesByUser.get(user);
+ try {
+ final PackageInfo pkgInfo = mContext.getPackageManager().getPackageInfo(
+ appInfo.packageName, PackageManager.GET_SIGNING_CERTIFICATES);
+ final Signature[] signatures = getLatestSignatures(pkgInfo.signingInfo);
+ // If any of the signatures appears in vouches, then we don't show hidden app
+ for (Signature signature : signatures) {
+ final String certDigest = computePackageCertDigest(signature);
+ if (vouches.contains(certDigest)) {
+ return false;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Should not happen
+ }
return true;
}
+ @VisibleForTesting
+ static String computePackageCertDigest(Signature signature) {
+ MessageDigest messageDigest;
+ try {
+ messageDigest = MessageDigest.getInstance("SHA1");
+ } catch (NoSuchAlgorithmException e) {
+ // Should not happen
+ return null;
+ }
+ messageDigest.update(signature.toByteArray());
+ final byte[] digest = messageDigest.digest();
+ return ByteStringUtils.toHexString(digest);
+ }
+
+ @VisibleForTesting
+ static Signature[] getLatestSignatures(SigningInfo signingInfo) {
+ if (signingInfo.hasMultipleSigners()) {
+ return signingInfo.getApkContentsSigners();
+ } else {
+ final Signature[] signatures = signingInfo.getSigningCertificateHistory();
+ return new Signature[]{signatures[0]};
+ }
+ }
+
+ private void updateVouches(String packageName, UserHandle user) {
+ final PackageManagerInternal pmInt =
+ LocalServices.getService(PackageManagerInternal.class);
+ ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName,
+ PackageManager.GET_META_DATA, Binder.getCallingUid(), user.getIdentifier());
+ if (appInfo == null) {
+ Log.w(TAG, "appInfo " + packageName + " is null");
+ return;
+ }
+ updateVouches(appInfo, user);
+ }
+
+ private void updateVouches(ApplicationInfo appInfo, UserHandle user) {
+ if (appInfo == null || appInfo.metaData == null) {
+ // No meta-data
+ return;
+ }
+ int tokenResourceId = appInfo.metaData.getInt(LauncherApps.VOUCHED_CERTS_KEY);
+ if (tokenResourceId == 0) {
+ // No xml file
+ return;
+ }
+ mVouchProviders.add(appInfo.packageName);
+ Set<String> vouches = mVouchedSignaturesByUser.get(user);
+ try {
+ List<String> signatures = Arrays.asList(
+ mContext.getPackageManager().getResourcesForApplication(
+ appInfo.packageName).getStringArray(tokenResourceId));
+ for (String signature : signatures) {
+ vouches.add(signature.toUpperCase());
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Should not happen
+ }
+ }
+
+ private void initVouchedSignatures(UserHandle user) {
+ synchronized (mVouchedSignaturesLocked) {
+ if (mVouchedSignaturesByUser.contains(user)) {
+ return;
+ }
+ final long startTime = mStatLogger.getTime();
+
+ Set<String> vouches = Collections.newSetFromMap(
+ new ConcurrentHashMap<String, Boolean>());
+
+ final int callingUid = injectBinderCallingUid();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ final PackageManagerInternal pmInt =
+ LocalServices.getService(PackageManagerInternal.class);
+ List<ApplicationInfo> installedPackages = pmInt.getInstalledApplications(
+ PackageManager.GET_META_DATA, user.getIdentifier(), callingUid);
+ for (ApplicationInfo appInfo : installedPackages) {
+ updateVouches(appInfo, user);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ mVouchedSignaturesByUser.putIfAbsent(user, vouches);
+ mStatLogger.logDurationStat(Stats.INIT_VOUCHED_SIGNATURES, startTime);
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
+ mStatLogger.dump(pw, " ");
+ }
+
@Override
public ActivityInfo resolveActivity(
String callingPackage, ComponentName component, UserHandle user)
@@ -760,6 +909,18 @@ public class LauncherAppsService extends SystemService {
mCallbackHandler.post(r);
}
+ private class VouchesChangedMonitor extends PackageMonitor {
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ updateVouches(packageName, new UserHandle(getChangingUserId()));
+ }
+
+ @Override
+ public void onPackageModified(String packageName) {
+ updateVouches(packageName, new UserHandle(getChangingUserId()));
+ }
+ }
+
private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
// TODO Simplify with lambdas.
diff --git a/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java
new file mode 100644
index 000000000000..d7dc58dbc213
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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.server.pm;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.pm.PackageParser;
+import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@Presubmit
+@RunWith(MockitoJUnitRunner.class)
+public class LauncherAppsServiceTest {
+
+ private static final Signature SIGNATURE_1 = new Signature(new byte[]{0x00, 0x01, 0x02, 0x03});
+ private static final Signature SIGNATURE_2 = new Signature(new byte[]{0x04, 0x05, 0x06, 0x07});
+ private static final Signature SIGNATURE_3 = new Signature(new byte[]{0x08, 0x09, 0x10, 0x11});
+
+ @Test
+ public void testComputePackageCertDigest() {
+ String digest = LauncherAppsService.LauncherAppsImpl.computePackageCertDigest(SIGNATURE_1);
+ assertEquals("A02A05B025B928C039CF1AE7E8EE04E7C190C0DB", digest);
+ }
+
+ @Test
+ public void testGetLatestSignaturesWithSingleCert() {
+ SigningInfo signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[]{SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null));
+ Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
+ signingInfo);
+ assertEquals(1, signatures.length);
+ assertEquals(SIGNATURE_1, signatures[0]);
+ }
+
+ @Test
+ public void testGetLatestSignaturesWithMultiCert() {
+ SigningInfo signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[]{SIGNATURE_1, SIGNATURE_2},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ null));
+ Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
+ signingInfo);
+ assertEquals(2, signatures.length);
+ assertEquals(SIGNATURE_1, signatures[0]);
+ assertEquals(SIGNATURE_2, signatures[1]);
+ }
+
+ @Test
+ public void testGetLatestSignaturesWithCertHistory() {
+ SigningInfo signingInfo = new SigningInfo(
+ new PackageParser.SigningDetails(
+ new Signature[]{SIGNATURE_1},
+ PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+ null,
+ new Signature[]{SIGNATURE_2, SIGNATURE_3}));
+ Signature[] signatures = LauncherAppsService.LauncherAppsImpl.getLatestSignatures(
+ signingInfo);
+ assertEquals(1, signatures.length);
+ assertEquals(SIGNATURE_2, signatures[0]);
+ }
+
+}