Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7106cc31 authored by Ricky Wai's avatar Ricky Wai Committed by Android (Google) Code Review
Browse files

Merge "Suppress injected launcher activity for apps that are vouched"

parents 5a516da0 e346a49a
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -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;
+164 −3
Original line number Diff line number Diff line
@@ -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,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(
@@ -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.
+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]);
    }

}