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