1 /*
2  * Copyright (C) 2021 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.security.cts.CVE_2021_0481;
18 
19 import org.junit.Before;
20 import org.junit.Test;
21 import org.junit.runner.RunWith;
22 
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.PackageManager;
26 import android.content.pm.ResolveInfo;
27 import android.os.SystemClock;
28 import android.util.Log;
29 
30 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
31 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
32 import androidx.test.filters.SdkSuppress;
33 import androidx.test.runner.AndroidJUnit4;
34 import androidx.test.uiautomator.By;
35 import androidx.test.uiautomator.UiDevice;
36 import androidx.test.uiautomator.UiObject2;
37 import androidx.test.uiautomator.Until;
38 import androidx.test.uiautomator.BySelector;
39 
40 import java.util.List;
41 
42 import static org.hamcrest.CoreMatchers.equalTo;
43 import static org.hamcrest.CoreMatchers.is;
44 import static org.hamcrest.CoreMatchers.notNullValue;
45 import static org.junit.Assert.assertThat;
46 import static org.junit.Assert.assertFalse;
47 import static org.junit.Assume.assumeNoException;
48 
49 /**
50  * launch "Settings" app
51  * set up user photo
52  */
53 @RunWith(AndroidJUnit4.class)
54 public class DeviceTest {
55 
56   class ClickableNotFound extends Exception{
ClickableNotFound(String s)57     public ClickableNotFound(String s){
58       super(s);
59     }
60   }
61 
62   private static final String BASIC_SAMPLE_PACKAGE
63           = "android.security.cts.CVE_2021_0481";
64   private static final int LAUNCH_TIMEOUT_MS = 20000;
65   private static final String TAG = "TAG_2021_0481";
66   private static final int IS_FOUND_FLAG = 1;          // 0001
67   private static final int IS_CHECKED_FLAG = 2;        // 0010
68   private UiDevice mDevice;
69 
70   @Test
testUserPhotoSetUp()71   public void testUserPhotoSetUp() {
72 
73     //set mDevice and go to homescreen
74     mDevice = UiDevice.getInstance(getInstrumentation());
75     mDevice.pressHome();
76 
77     //start "Settings" app
78     Intent myIntent = new Intent("android.settings.USER_SETTINGS");
79                                 //android.provider.Settings.ACTION_USER_SETTINGS
80     myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
81     try{
82       getApplicationContext().startActivity(myIntent);
83     } catch(android.content.ActivityNotFoundException e){
84       Log.d(TAG, "Activity to be tested  doesn't exist. Test will pass.");
85       return;
86     }
87 
88     //wait for "User Settings" activity to appear.
89     SystemClock.sleep(6000);
90 
91     //perform UI test steps
92     try {
93 
94       //in "Multiple users" activity showing "Use multiple users" switch
95       searchAndClick(mDevice, "android:id/switch_widget", 2000);
96 
97       //in "Multiple users" activity showing a list of current users,
98       //look for the first item "android:id/title" on the list showing "You(Owner)"
99       searchAndClick(mDevice, "android:id/title", 2000);
100 
101       //in "Profile Info" dialog window showing clickable user silhouette
102       //look for clickable android.widget.ImageView object with attributes:
103       // getContentDescription()=Select photo
104       // getResourceName()=com.android.settings:id/user_photo
105       searchAndClick(mDevice, "com.android.settings:id/user_photo", 2000);
106 
107       //in unnamed subdialog showing two options: "Take a photo" "Choose an image"
108       searchAndClick(mDevice, "Choose an image", 6000);
109 
110       //in "Browse Files in Other Apps" activity
111       searchAndClick(mDevice, "android.security.cts.CVE_2021_0481.EvilActivity", 5000);
112 
113       //Image is chosen as (evilActivity) so we are getting back to
114       //"Profile Info" dialog window showing clickable user silhouette
115       //end "Cancel" and "OK" buttons.
116       //look for "Cancel button and click it"
117       searchAndClick(mDevice, "Cancel", 2000);
118 
119     } catch (ClickableNotFound e){
120       Log.d(TAG, e.toString());
121       assumeNoException(e);
122     }
123     Log.d(TAG, "end of testUserPhotoSetUp()");
124   }
125 
126   //see what is on screen and click on object containing name
127   //throw exception if object not found
searchAndClick(UiDevice mDevice, String name, int timeOut)128   private void searchAndClick(UiDevice mDevice, String name, int timeOut) throws ClickableNotFound {
129 
130     int ret;
131     List<UiObject2> objects = mDevice.findObjects(By.clickable(true));
132     boolean found = false;
133     Log.d(TAG, "looking for " + name);
134     Log.d(TAG, "found " + String.valueOf(objects!=null ? objects.size() : 0) + " clickables");
135 
136     if(objects != null){
137       for (UiObject2 o : objects) {
138         if((ret=searchAndLog(o, name, "")) !=0 )
139         {
140           found=true;
141           Log.d(TAG, name + " found");
142           if((ret & IS_CHECKED_FLAG) == 0) {
143               o.click();
144               Log.d(TAG, name + " clicked");
145               SystemClock.sleep(timeOut); //wait for click result to appear onscreen
146           }
147           break; //to avoid androidx.test.uiautomator.StaleObjectException
148         }
149       }
150     }
151     if(!found) {
152       throw new ClickableNotFound("\"" + name + "\" not found to click on");
153     }
154   }
155 
156   //Search for 'name' in UiObject2
157   //returns int flags showing search result:
158   // IS_CHECKED_FLAG - 'name' matches o.getResourceName() and o.isSelected()==true
159   // IS_FOUND_FLAG - 'name' matches anything else
searchAndLog(UiObject2 o, String name, String prefix)160   private int searchAndLog(UiObject2 o, String name, String prefix){
161 
162     int ret = 0;
163     String lname = o.getText();
164     String cname = o.getClassName();
165     String cdesc = o.getContentDescription();
166     String rname = o.getResourceName();
167     boolean checked = o.isChecked();
168 
169     Log.d(TAG, prefix + "class=" + cname);
170     Log.d(TAG, prefix + "o.getText()=" + lname);
171     Log.d(TAG, prefix + "o.getContentDescription()=" + cdesc);
172     Log.d(TAG, prefix + "o.getResourceName()=" + rname);
173     Log.d(TAG, prefix + "o.getChildCount()=" + o.getChildCount());
174 
175     if( rname != null && rname.equals(name) && checked) {
176       ret |= IS_CHECKED_FLAG;
177     }
178     else if(lname != null && lname.equals(name) || cdesc != null && cdesc.equals(name) || rname != null && rname.equals(name) ) {
179       ret |= IS_FOUND_FLAG;
180     }
181 
182     if(ret != 0) {
183       Log.d(TAG, prefix + "found-->" + name);
184       return ret;
185     } else {
186       java.util.List<UiObject2> objects2 = o.getChildren();
187       if(objects2 != null && objects2.size() > 0 && prefix.length() < 50) {
188         for (UiObject2 o2 : objects2) {
189           if((ret=searchAndLog(o2, name, prefix + "__")) != 0){
190             return ret;
191           }
192         }
193       }
194     }
195     return ret;
196   }
197 
198 }
199 
200