1 /* 2 * Copyright (C) 2020 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 android.ext.services.resolver; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.eq; 22 import static org.mockito.Mockito.doNothing; 23 import static org.mockito.Mockito.when; 24 import static org.testng.Assert.assertThrows; 25 26 import android.content.Context; 27 import android.content.ContextWrapper; 28 import android.content.Intent; 29 import android.content.SharedPreferences; 30 import android.os.UserManager; 31 import android.service.resolver.ResolverTarget; 32 import android.test.ServiceTestCase; 33 import android.util.ArrayMap; 34 35 import com.google.common.collect.Range; 36 37 import org.junit.Before; 38 import org.junit.Test; 39 import org.mockito.Mock; 40 import org.mockito.Mockito; 41 import org.mockito.MockitoAnnotations; 42 43 import java.util.Arrays; 44 import java.util.List; 45 46 public class LRResolverRankerServiceTest extends ServiceTestCase<LRResolverRankerService> { 47 private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params"; 48 private static final String BIAS_PREF_KEY = "bias"; 49 private static final String VERSION_PREF_KEY = "version"; 50 private static final String LAUNCH_SCORE = "launch"; 51 private static final String TIME_SPENT_SCORE = "timeSpent"; 52 private static final String RECENCY_SCORE = "recency"; 53 private static final String CHOOSER_SCORE = "chooser"; 54 55 @Mock private Context mContext; 56 @Mock private SharedPreferences mSharedPreferences; 57 @Mock private SharedPreferences.Editor mEditor; 58 @Mock private UserManager mUserManager; 59 LRResolverRankerServiceTest()60 public LRResolverRankerServiceTest() { 61 super(LRResolverRankerService.class); 62 } 63 64 @Before setUp()65 public void setUp() throws Exception { 66 super.setUp(); 67 MockitoAnnotations.initMocks(this); 68 mContext = Mockito.spy(new ContextWrapper(getSystemContext())); 69 setContext(mContext); 70 71 Intent intent = new Intent(getContext(), LRResolverRankerService.class); 72 startService(intent); 73 } 74 75 @Test testInitModelUnderUserLocked()76 public void testInitModelUnderUserLocked() { 77 setUserLockedStatus(true); 78 createSharedPreferences(/* hasSharedPreferences */ true, /* version */ 1); 79 80 final LRResolverRankerService service = getService(); 81 service.onBind(new Intent("test")); 82 83 assertThat(service.mFeatureWeights).isNull(); 84 } 85 86 @Test testInitModelWhileNoSharedPreferences()87 public void testInitModelWhileNoSharedPreferences() { 88 setUserLockedStatus(false); 89 createSharedPreferences(/* hasSharedPreferences */ false, /* version */ 0); 90 91 final LRResolverRankerService service = getService(); 92 service.onBind(new Intent("test")); 93 94 final ArrayMap<String, Float> featureWeights = service.mFeatureWeights; 95 assertThat(featureWeights.get(LAUNCH_SCORE)).isEqualTo(2.5543f); 96 assertThat(featureWeights.get(TIME_SPENT_SCORE)).isEqualTo(2.8412f); 97 assertThat(featureWeights.get(RECENCY_SCORE)).isEqualTo(0.269f); 98 assertThat(featureWeights.get(CHOOSER_SCORE)).isEqualTo(4.2222f); 99 } 100 101 @Test testInitModelWhileVersionInSharedPreferencesSmallerThanCurrent()102 public void testInitModelWhileVersionInSharedPreferencesSmallerThanCurrent() { 103 setUserLockedStatus(false); 104 createSharedPreferences(/* hasSharedPreferences */ true, /* version */ 0); 105 106 final LRResolverRankerService service = getService(); 107 service.onBind(new Intent("test")); 108 109 final ArrayMap<String, Float> featureWeights = service.mFeatureWeights; 110 assertThat(featureWeights.get(LAUNCH_SCORE)).isEqualTo(2.5543f); 111 assertThat(featureWeights.get(TIME_SPENT_SCORE)).isEqualTo(2.8412f); 112 assertThat(featureWeights.get(RECENCY_SCORE)).isEqualTo(0.269f); 113 assertThat(featureWeights.get(CHOOSER_SCORE)).isEqualTo(4.2222f); 114 } 115 116 @Test testInitModelWhileVersionInSharedPreferencesNotSmallerThanCurrent()117 public void testInitModelWhileVersionInSharedPreferencesNotSmallerThanCurrent() { 118 setUserLockedStatus(false); 119 createSharedPreferences(/* hasSharedPreferences */ true, /* version */ 1); 120 121 final LRResolverRankerService service = getService(); 122 service.onBind(new Intent("test")); 123 124 final ArrayMap<String, Float> featureWeights = service.mFeatureWeights; 125 assertThat(featureWeights.get(LAUNCH_SCORE)).isEqualTo(0.1f); 126 assertThat(featureWeights.get(TIME_SPENT_SCORE)).isEqualTo(0.2f); 127 assertThat(featureWeights.get(RECENCY_SCORE)).isEqualTo(0.3f); 128 assertThat(featureWeights.get(CHOOSER_SCORE)).isEqualTo(0.4f); 129 } 130 131 @Test testOnPredictSharingProbabilitiesWhileServiceIsNotReady()132 public void testOnPredictSharingProbabilitiesWhileServiceIsNotReady() { 133 setUserLockedStatus(true); 134 createSharedPreferences(/* hasSharedPreferences */ true, /* version */ 1); 135 final LRResolverRankerService service = getService(); 136 service.onBind(new Intent("test")); 137 final List<ResolverTarget> targets = 138 Arrays.asList( 139 makeNewResolverTarget(/* recency */ 0.1f, /* timeSpent */ 0.2f, 140 /* launch */ 0.3f, /* chooser */ 0.4f, /* selectProb */ -1.0f), 141 makeNewResolverTarget(/* recency */ 0.4f, /* timeSpent */ 0.3f, 142 /* launch */ 0.2f, /* chooser */ 0.1f, /* selectProb */ -1.0f)); 143 144 assertThrows(IllegalStateException.class, 145 () -> service.onPredictSharingProbabilities(targets)); 146 } 147 148 @Test testOnPredictSharingProbabilitiesAfterUserUnlock()149 public void testOnPredictSharingProbabilitiesAfterUserUnlock() { 150 setUserLockedStatus(true); 151 createSharedPreferences(/* hasSharedPreferences */ true, /* version */ 1); 152 final LRResolverRankerService service = getService(); 153 service.onBind(new Intent("test")); 154 setUserLockedStatus(false); 155 final List<ResolverTarget> targets = 156 Arrays.asList( 157 makeNewResolverTarget(/* recency */ 0.1f, /* timeSpent */ 0.2f, 158 /* launch */ 0.3f, /* chooser */ 0.4f, /* selectProb */ -1.0f), 159 makeNewResolverTarget(/* recency */ 0.4f, /* timeSpent */ 0.3f, 160 /* launch */ 0.2f, /* chooser */ 0.1f, /* selectProb */ -1.0f)); 161 162 service.onPredictSharingProbabilities(targets); 163 164 assertThat(targets.get(0).getSelectProbability()).isIn(Range.closed(0f, 1.0f)); 165 assertThat(targets.get(1).getSelectProbability()).isIn(Range.closed(0f, 1.0f)); 166 } 167 168 @Test testOnPredictSharingProbabilities()169 public void testOnPredictSharingProbabilities() { 170 setUserLockedStatus(false); 171 createSharedPreferences(/* hasSharedPreferences */ false, /* version */ 0); 172 final LRResolverRankerService service = getService(); 173 service.onBind(new Intent("test")); 174 final List<ResolverTarget> targets = 175 Arrays.asList( 176 makeNewResolverTarget(/* recency */ 0.1f, /* timeSpent */ 0.2f, 177 /* launch */ 0.3f, /* chooser */ 0.4f, /* selectProb */ -1.0f), 178 makeNewResolverTarget(/* recency */ 0.4f, /* timeSpent */ 0.3f, 179 /* launch */ 0.2f, /* chooser */ 0.1f, /* selectProb */ -1.0f)); 180 181 service.onPredictSharingProbabilities(targets); 182 183 assertThat(targets.get(0).getSelectProbability()).isIn(Range.closed(0f, 1.0f)); 184 assertThat(targets.get(1).getSelectProbability()).isIn(Range.closed(0f, 1.0f)); 185 } 186 187 @Test testOnTrainRankingModelWhileServiceIsNotReady()188 public void testOnTrainRankingModelWhileServiceIsNotReady() { 189 setUserLockedStatus(true); 190 createSharedPreferences(/* hasSharedPreferences */ true, /* version */ 1); 191 final LRResolverRankerService service = getService(); 192 service.onBind(new Intent("test")); 193 final List<ResolverTarget> targets = 194 Arrays.asList( 195 makeNewResolverTarget(/* recency */ 0.1f, /* timeSpent */ 0.1f, 196 /* launch */ 0.1f, /* chooser */ 0.1f, /* selectProb */ 0.1f), 197 makeNewResolverTarget(/* recency */ 0.2f, /* timeSpent */ 0.2f, 198 /* launch */ 0.2f, /* chooser */ 0.2f, /* selectProb */ 0.2f), 199 makeNewResolverTarget(/* recency */ 0.3f, /* timeSpent */ 0.3f, 200 /* launch */ 0.3f, /* chooser */ 0.3f, /* selectProb */ 0.3f)); 201 202 assertThrows(IllegalStateException.class, 203 () -> service.onTrainRankingModel(targets, /* selectedPosition */ 0)); 204 } 205 206 @Test testOnTrainRankingModelAfterUserUnlock()207 public void testOnTrainRankingModelAfterUserUnlock() { 208 setUserLockedStatus(true); 209 createSharedPreferences(/* hasSharedPreferences */ true, /* version */ 1); 210 final LRResolverRankerService service = getService(); 211 service.onBind(new Intent("test")); 212 setUserLockedStatus(false); 213 final List<ResolverTarget> targets = 214 Arrays.asList( 215 makeNewResolverTarget(/* recency */ 0.1f, /* timeSpent */ 0.1f, 216 /* launch */ 0.1f, /* chooser */ 0.1f, /* selectProb */ 0.1f), 217 makeNewResolverTarget(/* recency */ 0.2f, /* timeSpent */ 0.2f, 218 /* launch */ 0.2f, /* chooser */ 0.2f, /* selectProb */ 0.2f), 219 makeNewResolverTarget(/* recency */ 0.3f, /* timeSpent */ 0.3f, 220 /* launch */ 0.3f, /* chooser */ 0.3f, /* selectProb */ 0.3f)); 221 222 service.onTrainRankingModel(targets, /* selectedPosition */ 0); 223 224 final ArrayMap<String, Float> featureWeights = service.mFeatureWeights; 225 assertThat(featureWeights.get(LAUNCH_SCORE)).isNotEqualTo(0.1f); 226 assertThat(featureWeights.get(TIME_SPENT_SCORE)).isNotEqualTo(0.2f); 227 assertThat(featureWeights.get(RECENCY_SCORE)).isNotEqualTo(0.3f); 228 assertThat(featureWeights.get(CHOOSER_SCORE)).isNotEqualTo(0.4f); 229 } 230 231 @Test testOnTrainRankingModelWhileSelectedPositionScoreIsNotHighest()232 public void testOnTrainRankingModelWhileSelectedPositionScoreIsNotHighest() { 233 setUserLockedStatus(false); 234 createSharedPreferences(/* hasSharedPreferences */ true, /* version */ 1); 235 final LRResolverRankerService service = getService(); 236 service.onBind(new Intent("test")); 237 final List<ResolverTarget> targets = 238 Arrays.asList( 239 makeNewResolverTarget(/* recency */ 0.1f, /* timeSpent */ 0.1f, 240 /* launch */ 0.1f, /* chooser */ 0.1f, /* selectProb */ 0.1f), 241 makeNewResolverTarget(/* recency */ 0.2f, /* timeSpent */ 0.2f, 242 /* launch */ 0.2f, /* chooser */ 0.2f, /* selectProb */ 0.2f), 243 makeNewResolverTarget(/* recency */ 0.3f, /* timeSpent */ 0.3f, 244 /* launch */ 0.3f, /* chooser */ 0.3f, /* selectProb */ 0.3f)); 245 246 service.onTrainRankingModel(targets, /* selectedPosition */ 0); 247 248 final ArrayMap<String, Float> featureWeights = service.mFeatureWeights; 249 assertThat(featureWeights.get(LAUNCH_SCORE)).isNotEqualTo(0.1f); 250 assertThat(featureWeights.get(TIME_SPENT_SCORE)).isNotEqualTo(0.2f); 251 assertThat(featureWeights.get(RECENCY_SCORE)).isNotEqualTo(0.3f); 252 assertThat(featureWeights.get(CHOOSER_SCORE)).isNotEqualTo(0.4f); 253 } 254 255 @Test testOnTrainRankingModelWhileSelectedPositionScoreIsHighest()256 public void testOnTrainRankingModelWhileSelectedPositionScoreIsHighest() { 257 setUserLockedStatus(false); 258 createSharedPreferences(/* hasSharedPreferences */ true, /* version */ 1); 259 final LRResolverRankerService service = getService(); 260 service.onBind(new Intent("test")); 261 final List<ResolverTarget> targets = 262 Arrays.asList( 263 makeNewResolverTarget(/* recency */ 0.1f, /* timeSpent */ 0.1f, 264 /* launch */ 0.1f, /* chooser */ 0.1f, /* selectProb */ 0.1f), 265 makeNewResolverTarget(/* recency */ 0.2f, /* timeSpent */ 0.2f, 266 /* launch */ 0.2f, /* chooser */ 0.2f, /* selectProb */ 0.2f), 267 makeNewResolverTarget(/* recency */ 0.3f, /* timeSpent */ 0.3f, 268 /* launch */ 0.3f, /* chooser */ 0.3f, /* selectProb */ 0.3f)); 269 270 service.onTrainRankingModel(targets, /* selectedPosition */ 2); 271 272 final ArrayMap<String, Float> featureWeights = service.mFeatureWeights; 273 assertThat(featureWeights.get(LAUNCH_SCORE)).isEqualTo(0.1f); 274 assertThat(featureWeights.get(TIME_SPENT_SCORE)).isEqualTo(0.2f); 275 assertThat(featureWeights.get(RECENCY_SCORE)).isEqualTo(0.3f); 276 assertThat(featureWeights.get(CHOOSER_SCORE)).isEqualTo(0.4f); 277 } 278 makeNewResolverTarget(float recency, float timeSpent, float launch, float chooser, float selectProb)279 private ResolverTarget makeNewResolverTarget(float recency, float timeSpent, float launch, 280 float chooser, float selectProb) { 281 ResolverTarget target = new ResolverTarget(); 282 target.setRecencyScore(recency); 283 target.setTimeSpentScore(timeSpent); 284 target.setLaunchScore(launch); 285 target.setChooserScore(chooser); 286 target.setSelectProbability(selectProb); 287 return target; 288 } 289 createSharedPreferences(boolean hasSharedPreferences, int version)290 private void createSharedPreferences(boolean hasSharedPreferences, int version) { 291 when(mContext.createCredentialProtectedStorageContext()).thenReturn(mContext); 292 if (!hasSharedPreferences) { 293 when(mContext.getSharedPreferences(/* name */ PARAM_SHARED_PREF_NAME + ".xml", 294 Context.MODE_PRIVATE)).thenReturn(null); 295 } else { 296 when(mContext.getSharedPreferences(/* name */ PARAM_SHARED_PREF_NAME + ".xml", 297 Context.MODE_PRIVATE)).thenReturn(mSharedPreferences); 298 when(mSharedPreferences.getInt(VERSION_PREF_KEY, 0)).thenReturn(version); 299 when(mSharedPreferences.getFloat(BIAS_PREF_KEY, 0)).thenReturn(0.0f); 300 when(mSharedPreferences.getFloat(LAUNCH_SCORE, 0)).thenReturn(0.1f); 301 when(mSharedPreferences.getFloat(TIME_SPENT_SCORE, 0)).thenReturn(0.2f); 302 when(mSharedPreferences.getFloat(RECENCY_SCORE, 0)).thenReturn(0.3f); 303 when(mSharedPreferences.getFloat(CHOOSER_SCORE, 0)).thenReturn(0.4f); 304 when(mSharedPreferences.edit()).thenReturn(mEditor); 305 doNothing().when(mEditor).apply(); 306 } 307 } 308 setUserLockedStatus(boolean locked)309 private void setUserLockedStatus(boolean locked) { 310 when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager); 311 when(mUserManager.isUserUnlocked()).thenReturn(!locked); 312 } 313 } 314