Loading services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java +23 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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()); Loading Loading @@ -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; }); } Loading services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java +137 −4 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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"); Loading Loading
services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java +23 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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()); Loading Loading @@ -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; }); } Loading
services/tests/servicestests/src/com/android/server/net/watchlist/WatchlistLoggingHandlerTests.java +137 −4 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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"); Loading