diff options
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]); + } + +} |