1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.job; 18 19 import static org.junit.Assert.assertTrue; 20 import static org.junit.Assert.fail; 21 import static org.junit.Assume.assumeFalse; 22 import static org.mockito.ArgumentMatchers.anyString; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.when; 25 26 import android.app.job.JobInfo; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.pm.PackageManagerInternal; 30 import android.os.Build; 31 import android.os.UserHandle; 32 import android.platform.test.annotations.Presubmit; 33 import android.util.ArraySet; 34 import android.util.Log; 35 import android.util.SparseArray; 36 37 import androidx.test.InstrumentationRegistry; 38 import androidx.test.filters.SmallTest; 39 import androidx.test.runner.AndroidJUnit4; 40 41 import com.android.server.LocalServices; 42 import com.android.server.job.controllers.JobStatus; 43 44 import org.junit.After; 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 49 @RunWith(AndroidJUnit4.class) 50 @SmallTest 51 @Presubmit 52 public class JobSetTest { 53 private static final String TAG = JobSetTest.class.getSimpleName(); 54 private static final int SECONDARY_USER_ID_1 = 12; 55 private static final int SECONDARY_USER_ID_2 = 13; 56 57 private Context mContext; 58 private ComponentName mComponent; 59 private JobStore.JobSet mJobSet; 60 61 @Before setUp()62 public void setUp() throws Exception { 63 mContext = InstrumentationRegistry.getTargetContext(); 64 mComponent = new ComponentName(mContext, JobStoreTest.class); 65 mJobSet = new JobStore.JobSet(); 66 final PackageManagerInternal pm = mock(PackageManagerInternal.class); 67 when(pm.getPackageTargetSdkVersion(anyString())) 68 .thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT); 69 LocalServices.removeServiceForTest(PackageManagerInternal.class); 70 LocalServices.addService(PackageManagerInternal.class, pm); 71 assumeFalse("Test cannot run in user " + mContext.getUserId(), 72 mContext.getUserId() == SECONDARY_USER_ID_1 73 || mContext.getUserId() == SECONDARY_USER_ID_2); 74 } 75 getJobStatusWithCallinUid(int jobId, int callingUid)76 private JobStatus getJobStatusWithCallinUid(int jobId, int callingUid) { 77 final JobInfo jobInfo = new JobInfo.Builder(jobId, mComponent) 78 .setPeriodic(10) 79 .setRequiresCharging(true) 80 .build(); 81 return JobStatus.createFromJobInfo(jobInfo, callingUid, mContext.getPackageName(), 82 mContext.getUserId(), "Namespace", "Test"); 83 } 84 85 @Test testBothMapsHaveSameJobs()86 public void testBothMapsHaveSameJobs() { 87 final int callingUid1 = UserHandle.getUid(SECONDARY_USER_ID_1, 1); 88 final int callingUid2 = UserHandle.getUid(SECONDARY_USER_ID_2, 1); 89 final JobStatus testJob1 = getJobStatusWithCallinUid(1, callingUid1); 90 final JobStatus testJob2 = getJobStatusWithCallinUid(2, callingUid2); 91 mJobSet.add(testJob1); 92 mJobSet.add(testJob2); 93 for (int i = 11; i <= 20; i++) { 94 mJobSet.add(getJobStatusWithCallinUid(i, (i%2 == 0) ? callingUid2 : callingUid1)); 95 } 96 assertHaveSameJobs(mJobSet.mJobsPerSourceUid, mJobSet.mJobs); 97 mJobSet.remove(testJob1); 98 mJobSet.remove(testJob2); 99 assertHaveSameJobs(mJobSet.mJobsPerSourceUid, mJobSet.mJobs); 100 mJobSet.removeJobsOfUnlistedUsers(new int[] {mContext.getUserId(), SECONDARY_USER_ID_1}); 101 assertHaveSameJobs(mJobSet.mJobsPerSourceUid, mJobSet.mJobs); 102 mJobSet.removeJobsOfUnlistedUsers(new int[] {mContext.getUserId()}); 103 assertTrue("mJobs should be empty", mJobSet.mJobs.size() == 0); 104 assertTrue("mJobsPerSourceUid should be empty", mJobSet.mJobsPerSourceUid.size() == 0); 105 } 106 assertHaveSameJobs(SparseArray<ArraySet<JobStatus>> map1, SparseArray<ArraySet<JobStatus>> map2)107 private static void assertHaveSameJobs(SparseArray<ArraySet<JobStatus>> map1, 108 SparseArray<ArraySet<JobStatus>> map2) { 109 final ArraySet<JobStatus> set1 = new ArraySet<>(); 110 final ArraySet<JobStatus> set2 = new ArraySet<>(); 111 int size1 = 0; 112 for (int i = 0; i < map1.size(); i++) { 113 final ArraySet<JobStatus> jobs = map1.valueAt(i); 114 if (jobs == null) return; 115 size1 += jobs.size(); 116 set1.addAll(jobs); 117 } 118 for (int i = 0; i < map2.size(); i++) { 119 final ArraySet<JobStatus> jobs = map2.valueAt(i); 120 if (jobs == null) return; 121 size1 -= jobs.size(); 122 set2.addAll(jobs); 123 } 124 if (size1 != 0 || !set1.equals(set2)) { 125 dump("map1", map1); 126 dump("map2", map2); 127 fail("Both maps have different sets of jobs"); 128 } 129 } 130 dump(String prefix, SparseArray<ArraySet<JobStatus>> jobMap)131 private static void dump(String prefix, SparseArray<ArraySet<JobStatus>> jobMap) { 132 final StringBuilder str = new StringBuilder(); 133 for (int i = 0; i < jobMap.size(); i++) { 134 final ArraySet<JobStatus> jobs = jobMap.valueAt(i); 135 if (jobs == null) return; 136 str.append("[Key: " + jobMap.keyAt(i) + ", Value: {"); 137 for (int j = 0; j < jobs.size(); j++) { 138 final JobStatus job = jobs.valueAt(j); 139 str.append("(s=" + job.getSourceUid() + ", c=" + job.getUid() + "), "); 140 } 141 str.append("}], "); 142 } 143 Log.d(TAG, prefix + ": " + str.toString()); 144 } 145 146 @After tearDown()147 public void tearDown() throws Exception { 148 LocalServices.removeServiceForTest(PackageManagerInternal.class); 149 } 150 } 151