Loading core/java/android/content/pm/LauncherApps.java +11 −0 Original line number Diff line number Diff line Loading @@ -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; Loading services/core/java/com/android/server/pm/LauncherAppsService.java +164 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -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; Loading @@ -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); Loading @@ -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 Loading Loading @@ -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); Loading @@ -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, Loading @@ -387,12 +419,129 @@ 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( Loading Loading @@ -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. Loading services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java 0 → 100644 +87 −0 Original line number Diff line number Diff line /* * 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]); } } Loading
core/java/android/content/pm/LauncherApps.java +11 −0 Original line number Diff line number Diff line Loading @@ -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; Loading
services/core/java/com/android/server/pm/LauncherAppsService.java +164 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading Loading @@ -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; Loading @@ -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); Loading @@ -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 Loading Loading @@ -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); Loading @@ -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, Loading @@ -387,12 +419,129 @@ 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( Loading Loading @@ -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. Loading
services/tests/servicestests/src/com/android/server/pm/LauncherAppsServiceTest.java 0 → 100644 +87 −0 Original line number Diff line number Diff line /* * 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]); } }