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 com.android.systemui.plugin.globalactions.wallet; 18 19 import static android.service.quickaccesswallet.QuickAccessWalletService.SERVICE_INTERFACE; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.robolectric.Shadows.shadowOf; 24 25 import android.Manifest; 26 import android.app.ActivityManager; 27 import android.content.ComponentName; 28 import android.content.ContentResolver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.pm.ApplicationInfo; 32 import android.content.pm.PackageInfo; 33 import android.content.pm.PackageManager; 34 import android.content.pm.ResolveInfo; 35 import android.content.pm.ServiceInfo; 36 import android.provider.Settings; 37 import android.service.quickaccesswallet.QuickAccessWalletClient; 38 39 import androidx.test.core.app.ApplicationProvider; 40 41 import com.android.internal.widget.LockPatternUtils; 42 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.mockito.MockitoAnnotations; 47 import org.robolectric.RobolectricTestRunner; 48 import org.robolectric.RuntimeEnvironment; 49 import org.robolectric.annotation.Config; 50 import org.robolectric.annotation.Implementation; 51 import org.robolectric.annotation.Implements; 52 import org.robolectric.annotation.Resetter; 53 import org.robolectric.shadow.api.Shadow; 54 import org.robolectric.shadows.ShadowLog; 55 56 /** 57 * Ensures compatibility between the {@link QuickAccessWalletClient} and the plugin 58 */ 59 @RunWith(RobolectricTestRunner.class) 60 @Config(shadows = { 61 QuickAccessWalletClientTest.ShadowActivityManager.class, 62 QuickAccessWalletClientTest.ShadowLockPatternUtils.class}) 63 public class QuickAccessWalletClientTest { 64 65 private final Context mContext = ApplicationProvider.getApplicationContext(); 66 private QuickAccessWalletClient mWalletClient; 67 68 @Before setUp()69 public void setUp() { 70 MockitoAnnotations.initMocks(this); 71 ShadowLog.stream = System.out; 72 } 73 74 @Test isWalletServiceAvailable_serviceAvailable_returnsTrue()75 public void isWalletServiceAvailable_serviceAvailable_returnsTrue() { 76 setDefaultPaymentApp(mContext.getPackageName()); 77 registerWalletService(); 78 79 mWalletClient = QuickAccessWalletClient.create(mContext); 80 81 assertThat(mWalletClient.isWalletServiceAvailable()).isTrue(); 82 } 83 84 @Test isWalletServiceAvailable_returnsFalseIfServiceUnavailable()85 public void isWalletServiceAvailable_returnsFalseIfServiceUnavailable() { 86 setDefaultPaymentApp(mContext.getPackageName()); 87 88 mWalletClient = QuickAccessWalletClient.create(mContext); 89 90 assertThat(mWalletClient.isWalletServiceAvailable()).isFalse(); 91 } 92 93 @Test isWalletServiceAvailable_returnsFalseIfNfcPaymentAppIsAnotherApp()94 public void isWalletServiceAvailable_returnsFalseIfNfcPaymentAppIsAnotherApp() { 95 setDefaultPaymentApp("foo.bar"); 96 registerWalletService(); 97 98 mWalletClient = QuickAccessWalletClient.create(mContext); 99 100 assertThat(mWalletClient.isWalletServiceAvailable()).isFalse(); 101 } 102 103 @Test isWalletFeatureAvailable_happyCase()104 public void isWalletFeatureAvailable_happyCase() { 105 setDefaultPaymentApp(mContext.getPackageName()); 106 registerWalletService(); 107 WalletPluginService.updateSettingsFeatureAvailability(mContext, true); 108 ContentResolver cr = mContext.getContentResolver(); 109 Settings.Secure.putInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 1); 110 111 mWalletClient = QuickAccessWalletClient.create(mContext); 112 113 assertThat(mWalletClient.isWalletFeatureAvailable()).isTrue(); 114 } 115 116 @Test isWalletFeatureAvailable_wrongUser()117 public void isWalletFeatureAvailable_wrongUser() { 118 setDefaultPaymentApp(mContext.getPackageName()); 119 registerWalletService(); 120 WalletPluginService.updateSettingsFeatureAvailability(mContext, true); 121 ContentResolver cr = mContext.getContentResolver(); 122 Settings.Secure.putInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 1); 123 ShadowActivityManager.setCurrentUser(11); 124 125 mWalletClient = QuickAccessWalletClient.create(mContext); 126 127 assertThat(mWalletClient.isWalletFeatureAvailable()).isFalse(); 128 } 129 130 @Test isWalletFeatureAvailable_userSetupIncomplete()131 public void isWalletFeatureAvailable_userSetupIncomplete() { 132 setDefaultPaymentApp(mContext.getPackageName()); 133 registerWalletService(); 134 WalletPluginService.updateSettingsFeatureAvailability(mContext, true); 135 // do not set user setup complete 136 137 mWalletClient = QuickAccessWalletClient.create(mContext); 138 139 assertThat(mWalletClient.isWalletFeatureAvailable()).isFalse(); 140 } 141 142 @Test isWalletFeatureAvailable_globalActionsPanelDisabled()143 public void isWalletFeatureAvailable_globalActionsPanelDisabled() { 144 setDefaultPaymentApp(mContext.getPackageName()); 145 registerWalletService(); 146 WalletPluginService.updateSettingsFeatureAvailability(mContext, true); 147 ContentResolver cr = mContext.getContentResolver(); 148 Settings.Secure.putInt(cr, 149 Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED, 0); 150 Settings.Secure.putInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 1); 151 152 mWalletClient = QuickAccessWalletClient.create(mContext); 153 154 assertThat(mWalletClient.isWalletFeatureAvailable()).isFalse(); 155 } 156 157 @Test isWalletFeatureAvailable_userInLockdown()158 public void isWalletFeatureAvailable_userInLockdown() { 159 setDefaultPaymentApp(mContext.getPackageName()); 160 registerWalletService(); 161 WalletPluginService.updateSettingsFeatureAvailability(mContext, true); 162 ContentResolver cr = mContext.getContentResolver(); 163 Settings.Secure.putInt(cr, Settings.Secure.USER_SETUP_COMPLETE, 1); 164 ShadowLockPatternUtils.sIsUserInLockdown = true; 165 166 mWalletClient = QuickAccessWalletClient.create(mContext); 167 168 assertThat(mWalletClient.isWalletFeatureAvailable()).isFalse(); 169 } 170 171 @Test isWalletFeatureAvailableWhenDeviceLocked()172 public void isWalletFeatureAvailableWhenDeviceLocked() { 173 ContentResolver cr = mContext.getContentResolver(); 174 mWalletClient = QuickAccessWalletClient.create(mContext); 175 176 Settings.Secure.putInt(cr, Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 1); 177 178 assertThat(mWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).isTrue(); 179 180 Settings.Secure.putInt(cr, Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 0); 181 182 assertThat(mWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).isFalse(); 183 } 184 setDefaultPaymentApp(String pkg)185 private void setDefaultPaymentApp(String pkg) { 186 ComponentName nfcComponent = new ComponentName(pkg, "WalletService"); 187 Settings.Secure.putString(mContext.getContentResolver(), "nfc_payment_default_component", 188 nfcComponent.flattenToString()); 189 } 190 registerWalletService()191 private void registerWalletService() { 192 Intent queryIntent = new Intent(SERVICE_INTERFACE).setPackage(mContext.getPackageName()); 193 ServiceInfo serviceInfo = new ServiceInfo(); 194 serviceInfo.exported = true; 195 serviceInfo.applicationInfo = new ApplicationInfo(); 196 serviceInfo.applicationInfo.packageName = mContext.getPackageName(); 197 serviceInfo.name = "QuickAccessWalletService"; 198 serviceInfo.permission = Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE; 199 ResolveInfo resolveInfo = new ResolveInfo(); 200 resolveInfo.serviceInfo = serviceInfo; 201 PackageManager rpm = mContext.getPackageManager(); 202 PackageInfo packageInfo = new PackageInfo(); 203 packageInfo.packageName = mContext.getPackageName(); 204 packageInfo.services = new ServiceInfo[]{serviceInfo}; 205 shadowOf(rpm).addResolveInfoForIntent(queryIntent, resolveInfo); 206 } 207 208 @Implements(LockPatternUtils.class) 209 public static class ShadowLockPatternUtils { 210 private static boolean sIsUserInLockdown = false; 211 212 @Implementation isUserInLockdown(int userId)213 public boolean isUserInLockdown(int userId) { 214 return sIsUserInLockdown; 215 } 216 } 217 218 @Implements(ActivityManager.class) 219 public static class ShadowActivityManager { 220 private static int sCurrentUserId = 0; 221 222 @Resetter reset()223 public void reset() { 224 sCurrentUserId = 0; 225 } 226 227 @Implementation getCurrentUser()228 protected static int getCurrentUser() { 229 return sCurrentUserId; 230 } 231 setCurrentUser(int userId)232 public static void setCurrentUser(int userId) { 233 sCurrentUserId = userId; 234 } 235 getShadow()236 public static ShadowActivityManager getShadow() { 237 return (ShadowActivityManager) Shadow.extract( 238 RuntimeEnvironment.application.getSystemService(ActivityManager.class)); 239 } 240 } 241 242 } 243