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

Commit 52ee130c authored by Ricky Wai's avatar Ricky Wai
Browse files

Handle non-primary user only apps in watchlist report

Bug: 72995507
Test: Install an app in work profile and able to generate watchlist
report
Test: runtest frameworks-services -c com.android.server.net.watchlist.WatchlistLoggingHandlerTests

Change-Id: I01e4e3986b9a5db7cb9929a5924a4bf7b13bbcd0
parent 292fa644
Loading
Loading
Loading
Loading
+23 −7
Original line number Diff line number Diff line
@@ -22,12 +22,14 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Slog;
@@ -238,20 +240,34 @@ class WatchlistLoggingHandler extends Handler {
     * if an app is really visited C&C site.
     * (2) App digests that previously recorded in database.
     */
    private List<String> getAllDigestsForReport(WatchlistReportDbHelper.AggregatedResult record) {
    @VisibleForTesting
    List<String> getAllDigestsForReport(WatchlistReportDbHelper.AggregatedResult record) {
        // Step 1: Get all installed application digests.
        final List<UserInfo> users = ((UserManager) mContext.getSystemService(
                Context.USER_SERVICE)).getUsers();
        final int totalUsers = users.size();
        final List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
                PackageManager.MATCH_ANY_USER | PackageManager.MATCH_ALL);
        final HashSet<String> result = new HashSet<>(apps.size() + record.appDigestCNCList.size());
        final int size = apps.size();
        for (int i = 0; i < size; i++) {
            byte[] digest = getDigestFromUid(apps.get(i).uid);
            if (digest == null) {
            final int appUid = apps.get(i).uid;
            boolean added = false;
            // As the uid returned by getInstalledApplications() is for primary user only, it
            // may exist in secondary users but not primary user, so we need to loop and see if
            // that user has the app enabled.
            for (int j = 0; j < totalUsers && !added; j++) {
                int uid = UserHandle.getUid(users.get(j).id, appUid);
                byte[] digest = getDigestFromUid(uid);
                if (digest != null) {
                    result.add(HexDump.toHexString(digest));
                    added = true;
                }
            }
            if (!added) {
                Slog.e(TAG, "Cannot get digest from uid: " + apps.get(i).uid
                        + ",pkg: " + apps.get(i).packageName);
                continue;
            }
            result.add(HexDump.toHexString(digest));
        }
        // Step 2: Add all digests from records
        result.addAll(record.appDigestCNCList.keySet());
@@ -288,9 +304,9 @@ class WatchlistLoggingHandler extends Handler {
                        return null;
                    }
                }
            } else {
                Slog.e(TAG, "Should not happen");
            }
            // Not able to find a package name for this uid, possibly the package is installed on
            // another user.
            return null;
        });
    }
+137 −4
Original line number Diff line number Diff line
@@ -16,18 +16,43 @@

package com.android.server.net.watchlist;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.doAnswer;

import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.FileUtils;
import android.os.Looper;
import android.os.UserManager;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.InstrumentationRegistry;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

/**
 * runtest frameworks-services -c com.android.server.net.watchlist.WatchlistLoggingHandlerTests
@@ -36,6 +61,114 @@ import java.util.Arrays;
@SmallTest
public class WatchlistLoggingHandlerTests {

    private static final String APK_A = "A.apk";
    private static final String APK_B = "B.apk";
    private static final String APK_A_CONTENT = "AAA";
    private static final String APK_B_CONTENT = "BBB";
    // Sha256 of "AAA"
    private static final String APK_A_CONTENT_HASH =
            "CB1AD2119D8FAFB69566510EE712661F9F14B83385006EF92AEC47F523A38358";
    // Sha256 of "BBB"
    private static final String APK_B_CONTENT_HASH =
            "DCDB704109A454784B81229D2B05F368692E758BFA33CB61D04C1B93791B0273";

    private Context mServiceContext;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);

        final Context context = InstrumentationRegistry.getContext();
        final UserManager mockUserManager = mock(UserManager.class);
        final PackageManager mockPackageManager = mock(PackageManager.class);

        // Context that will be used by WatchlistLoggingHandler
        mServiceContext = new ContextWrapper(context) {
            @Override
            public PackageManager getPackageManager() {
                return mockPackageManager;
            }

            @Override
            public Object getSystemService(String name) {
                switch (name) {
                    case Context.USER_SERVICE:
                        return mockUserManager;
                    default:
                        return super.getSystemService(name);
                }
            }
        };

        // Returns 2 users, user 0 and user 10
        doAnswer((InvocationOnMock invocation) -> {
            final ArrayList<UserInfo> info = new ArrayList<>();
            info.add(new UserInfo(0, "user1", 0));
            info.add(new UserInfo(10, "user2", 0));
            return info;
        }).when(mockUserManager).getUsers();

        // Returns 2 apps, with uid 1 and uid 2
        doAnswer((InvocationOnMock invocation) -> {
            final List<ApplicationInfo> result = new ArrayList<>();
            ApplicationInfo info1 = new ApplicationInfo();
            info1.uid = 1;
            result.add(info1);
            ApplicationInfo info2 = new ApplicationInfo();
            info2.uid = 2;
            result.add(info2);
            return result;
        }).when(mockPackageManager).getInstalledApplications(anyInt());

        // Uid 1 app with is installed in primary user and package name is "A"
        // Uid 2 app is installed in secondary user and package name is "B"
        doAnswer((InvocationOnMock invocation) -> {
            int uid = (int) invocation.getArguments()[0];
            if (uid == 1) {
                return new String[]{"A"};
            } else if (uid == 1000001) {
                return null;
            } else if (uid == 2) {
                return null;
            } else if (uid == 1000002) {
                return new String[]{"B"};
            }
            return null;
        }).when(mockPackageManager).getPackagesForUid(anyInt());

        String fileDir = InstrumentationRegistry.getContext().getFilesDir().getAbsolutePath();
        // Simulate app's apk file path
        doAnswer((InvocationOnMock invocation) -> {
            String pkg = (String) invocation.getArguments()[0];
            PackageInfo result = new PackageInfo();
            result.applicationInfo = new ApplicationInfo();
            result.applicationInfo.publicSourceDir = fileDir + "/" + pkg + ".apk";
            return result;
        }).when(mockPackageManager).getPackageInfoAsUser(anyString(), anyInt(), anyInt());

        FileUtils.bytesToFile(fileDir + "/" + APK_A, APK_A_CONTENT.getBytes());
        FileUtils.bytesToFile(fileDir + "/" + APK_B, APK_B_CONTENT.getBytes());
    }

    @After
    public void tearDown() {
        String fileDir = InstrumentationRegistry.getContext().getFilesDir().getAbsolutePath();
        new File(fileDir, APK_A).delete();
        new File(fileDir, APK_B).delete();
    }

    @Test
    public void testWatchlistLoggingHandler_getAllDigestsForReportWithMultiUsers()
            throws Exception {
        List<String> result = new WatchlistLoggingHandler(mServiceContext,
                Looper.getMainLooper()).getAllDigestsForReport(
                new WatchlistReportDbHelper.AggregatedResult(new HashSet<String>(), null,
                        new HashMap<String, String>()));
        assertEquals(2, result.size());
        assertEquals(APK_A_CONTENT_HASH, result.get(0));
        assertEquals(APK_B_CONTENT_HASH, result.get(1));
    }

    @Test
    public void testWatchlistLoggingHandler_getAllSubDomains() throws Exception {
        String[] subDomains = WatchlistLoggingHandler.getAllSubDomains("abc.def.gh.i.jkl.mm");