• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 package com.android.cts.launchertests;
17 
18 import static org.junit.Assert.assertNotEquals;
19 
20 import android.app.Instrumentation;
21 import android.content.BroadcastReceiver;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.ServiceConnection;
27 import android.content.pm.ActivityInfo;
28 import android.content.pm.ApplicationInfo;
29 import android.content.pm.LauncherActivityInfo;
30 import android.content.pm.LauncherApps;
31 import android.content.pm.PackageManager;
32 import android.content.pm.PackageManager.NameNotFoundException;
33 import android.net.Uri;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.os.IBinder;
37 import android.os.Looper;
38 import android.os.Message;
39 import android.os.Messenger;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.test.AndroidTestCase;
43 
44 import androidx.test.InstrumentationRegistry;
45 
46 import java.util.List;
47 import java.util.concurrent.Semaphore;
48 import java.util.concurrent.TimeUnit;
49 
50 /**
51  * Tests for LauncherApps service
52  */
53 public class LauncherAppsTests extends AndroidTestCase {
54 
55     public static final String SIMPLE_APP_PACKAGE = "com.android.cts.launcherapps.simpleapp";
56     private static final String NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE =
57             "com.android.cts.nolaunchableactivityapp";
58     private static final String NO_COMPONENT_APP_PACKAGE =
59             "com.android.cts.nocomponentapp";
60     private static final String NO_PERMISSION_APP_PACKAGE =
61             "com.android.cts.nopermissionapp";
62 
63     private static final String SYNTHETIC_APP_DETAILS_ACTIVITY = "android.app.AppDetailsActivity";
64 
65     public static final String USER_EXTRA = "user_extra";
66     public static final String PACKAGE_EXTRA = "package_extra";
67     public static final String REPLY_EXTRA = "reply_extra";
68 
69     private static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
70 
71     public static final int MSG_RESULT = 0;
72     public static final int MSG_CHECK_PACKAGE_ADDED = 1;
73     public static final int MSG_CHECK_PACKAGE_REMOVED = 2;
74     public static final int MSG_CHECK_PACKAGE_CHANGED = 3;
75     public static final int MSG_CHECK_NO_PACKAGE_ADDED = 4;
76 
77     public static final int RESULT_PASS = 1;
78     public static final int RESULT_FAIL = 2;
79     public static final int RESULT_TIMEOUT = 3;
80 
81     private LauncherApps mLauncherApps;
82     private UserHandle mUser;
83     private Instrumentation mInstrumentation;
84     private Messenger mService;
85     private Connection mConnection;
86     private Result mResult;
87     private Messenger mResultMessenger;
88 
89     @Override
setUp()90     protected void setUp() throws Exception {
91         super.setUp();
92         mInstrumentation = InstrumentationRegistry.getInstrumentation();
93         Bundle arguments = InstrumentationRegistry.getArguments();
94         UserManager userManager = (UserManager) mInstrumentation.getContext().getSystemService(
95                 Context.USER_SERVICE);
96         mUser = getUserHandleArgument(userManager, "testUser", arguments);
97         mLauncherApps = (LauncherApps) mInstrumentation.getContext().getSystemService(
98                 Context.LAUNCHER_APPS_SERVICE);
99 
100         final Intent intent = new Intent();
101         intent.setComponent(new ComponentName("com.android.cts.launchertests.support",
102                         "com.android.cts.launchertests.support.LauncherCallbackTestsService"));
103 
104         mConnection = new Connection();
105         mInstrumentation.getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
106         mConnection.waitForService();
107         mResult = new Result(Looper.getMainLooper());
108         mResultMessenger = new Messenger(mResult);
109     }
110 
testGetActivitiesForUserFails()111     public void testGetActivitiesForUserFails() throws Exception {
112         expectSecurityException(() -> mLauncherApps.getActivityList(null, mUser),
113                 "getActivities for non-profile user failed to throw exception");
114     }
115 
testSimpleAppInstalledForUser()116     public void testSimpleAppInstalledForUser() throws Exception {
117         List<LauncherActivityInfo> activities =
118                 mLauncherApps.getActivityList(null, mUser);
119         // Check simple app is there.
120         boolean foundSimpleApp = false;
121         for (LauncherActivityInfo activity : activities) {
122             if (activity.getComponentName().getPackageName().equals(
123                     SIMPLE_APP_PACKAGE)) {
124                 foundSimpleApp = true;
125             }
126             assertTrue(activity.getUser().equals(mUser));
127         }
128         assertTrue(foundSimpleApp);
129 
130         // Also make sure getApplicationInfo works too.
131         final ApplicationInfo ai =
132                 mLauncherApps.getApplicationInfo(SIMPLE_APP_PACKAGE, /* flags= */ 0, mUser);
133         assertEquals(SIMPLE_APP_PACKAGE, ai.packageName);
134         assertEquals(mUser, UserHandle.getUserHandleForUid(ai.uid));
135     }
136 
testAccessPrimaryProfileFromManagedProfile()137     public void testAccessPrimaryProfileFromManagedProfile() throws Exception {
138         assertTrue(mLauncherApps.getActivityList(null, mUser).isEmpty());
139 
140         expectNameNotFoundException(
141                 () -> mLauncherApps.getApplicationInfo(SIMPLE_APP_PACKAGE, /* flags= */ 0, mUser),
142                 "get applicationInfo failed to throw name not found exception");
143         assertFalse(mLauncherApps.isPackageEnabled(SIMPLE_APP_PACKAGE, mUser));
144 
145         final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.android.com/"));
146         assertNull(mLauncherApps.resolveActivity(intent, mUser));
147     }
148 
testGetProfiles_fromMainProfile()149     public void testGetProfiles_fromMainProfile() {
150         final List<UserHandle> profiles = mLauncherApps.getProfiles();
151         assertEquals(2, profiles.size());
152         assertTrue(profiles.contains(android.os.Process.myUserHandle()));
153         assertEquals(getContext().getSystemService(UserManager.class).getUserProfiles(),
154                 profiles);
155     }
156 
testGetProfiles_fromManagedProfile()157     public void testGetProfiles_fromManagedProfile() {
158         final List<UserHandle> profiles = mLauncherApps.getProfiles();
159         assertEquals(1, profiles.size());
160         assertEquals(android.os.Process.myUserHandle(), profiles.get(0));
161     }
162 
testPackageAddedCallbackForUser()163     public void testPackageAddedCallbackForUser() throws Throwable {
164         int result = sendMessageToCallbacksService(MSG_CHECK_PACKAGE_ADDED,
165                 mUser, SIMPLE_APP_PACKAGE);
166         assertEquals(RESULT_PASS, result);
167     }
168 
testPackageRemovedCallbackForUser()169     public void testPackageRemovedCallbackForUser() throws Throwable {
170         int result = sendMessageToCallbacksService(MSG_CHECK_PACKAGE_REMOVED,
171                 mUser, SIMPLE_APP_PACKAGE);
172         assertEquals(RESULT_PASS, result);
173     }
testPackageChangedCallbackForUser()174     public void testPackageChangedCallbackForUser() throws Throwable {
175         int result = sendMessageToCallbacksService(MSG_CHECK_PACKAGE_CHANGED,
176                 mUser, SIMPLE_APP_PACKAGE);
177         assertEquals(RESULT_PASS, result);
178     }
179 
testNoPackageAddedCallbackForUser()180     public void testNoPackageAddedCallbackForUser() throws Throwable {
181         int result = sendMessageToCallbacksService(MSG_CHECK_NO_PACKAGE_ADDED,
182                 mUser, SIMPLE_APP_PACKAGE);
183         assertEquals(RESULT_PASS, result);
184     }
185 
testLaunchNonExportActivityFails()186     public void testLaunchNonExportActivityFails() throws Exception {
187         expectSecurityException(() -> mLauncherApps.startMainActivity(new ComponentName(
188                 SIMPLE_APP_PACKAGE, SIMPLE_APP_PACKAGE + ".NonExportedActivity"),
189                 mUser, null, null),
190                 "starting non-exported activity failed to throw exception");
191     }
192 
testLaunchNonExportLauncherFails()193     public void testLaunchNonExportLauncherFails() throws Exception {
194         expectSecurityException(() -> mLauncherApps.startMainActivity(new ComponentName(
195                 SIMPLE_APP_PACKAGE, SIMPLE_APP_PACKAGE + ".NonLauncherActivity"),
196                 mUser, null, null),
197                 "starting non-launcher activity failed to throw exception");
198     }
199 
testLaunchMainActivity()200     public void testLaunchMainActivity() throws Exception {
201         ActivityLaunchedReceiver receiver = new ActivityLaunchedReceiver();
202         IntentFilter filter = new IntentFilter();
203         filter.addAction(ActivityLaunchedReceiver.ACTIVITY_LAUNCHED_ACTION);
204         mInstrumentation.getContext().registerReceiver(receiver, filter);
205         mLauncherApps.startMainActivity(new ComponentName(
206                 SIMPLE_APP_PACKAGE,
207                 SIMPLE_APP_PACKAGE + ".SimpleActivity"),
208                 mUser, null, null);
209         assertEquals(RESULT_PASS, receiver.waitForActivity());
210         mInstrumentation.getContext().unregisterReceiver(receiver);
211     }
212 
testReverseAccessNoThrow()213     public void testReverseAccessNoThrow() throws Exception {
214         // Trying to access the main profile from a managed profile -> shouldn't throw but
215         // should just return false.
216         assertFalse(mLauncherApps.isPackageEnabled("android", mUser));
217     }
218 
testNoLaunchableActivityAppHasAppDetailsActivityInjected()219     public void testNoLaunchableActivityAppHasAppDetailsActivityInjected() throws Exception {
220         // NoLaunchableActivityApp is installed for duration of this test - make sure
221         // it's present on the activity list, has the synthetic activity generated, and it's
222         // enabled and exported
223         assertActivityInjected(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE);
224     }
225 
testGetSetSyntheticAppDetailsActivityEnabled()226     public void testGetSetSyntheticAppDetailsActivityEnabled() throws Exception {
227         assertActivityInjected(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE);
228         PackageManager pm = mInstrumentation.getContext().getPackageManager();
229         try {
230             pm.setSyntheticAppDetailsActivityEnabled(mContext.getPackageName(), false);
231             fail("Should not able to change current app's app details activity state");
232         } catch (SecurityException e) {
233             // Expected: No permission
234         }
235         try {
236             pm.setSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE, false);
237             fail("Should not able to change other app's app details activity state");
238         } catch (SecurityException e) {
239             // Expected: No permission
240         }
241         mInstrumentation.getUiAutomation().adoptShellPermissionIdentity();
242         try {
243             assertTrue(
244                     pm.getSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE));
245             // Disable app details activity and assert if the change is applied
246             pm.setSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE, false);
247             assertFalse(
248                     pm.getSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE));
249             assertInjectedActivityNotFound(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE);
250             // Enable app details activity and assert if the change is applied
251             pm.setSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE, true);
252             assertTrue(
253                     pm.getSyntheticAppDetailsActivityEnabled(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE));
254             assertActivityInjected(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE);
255         } finally {
256             mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
257         }
258     }
259 
260 
testProfileOwnerLauncherActivityInjected()261     public void testProfileOwnerLauncherActivityInjected() throws Exception {
262         assertActivityInjected(MANAGED_PROFILE_PKG);
263     }
264 
testNoComponentAppNotInjected()265     public void testNoComponentAppNotInjected() throws Exception {
266         // NoComponentApp is installed for duration of this test - make sure
267         // it's NOT present on the activity list
268         assertInjectedActivityNotFound(NO_COMPONENT_APP_PACKAGE);
269     }
270 
testNoPermissionAppNotInjected()271     public void testNoPermissionAppNotInjected() throws Exception {
272         // NoPermissionApp is installed for duration of this test - make sure
273         // it's NOT present on the activity list
274         assertInjectedActivityNotFound(NO_PERMISSION_APP_PACKAGE);
275     }
276 
testDoPoNoTestAppInjectedActivityFound()277     public void testDoPoNoTestAppInjectedActivityFound() throws Exception {
278         // NoLaunchableActivityApp is installed for duration of this test - make sure
279         // it's NOT present on the activity list For example, DO / PO mode won't show icons.
280         // This test is being called by DeviceOwnerTest.
281         assertInjectedActivityNotFound(NO_LAUNCHABLE_ACTIVITY_APP_PACKAGE);
282     }
283 
testProfileOwnerInjectedActivityNotFound()284     public void testProfileOwnerInjectedActivityNotFound() throws Exception {
285         assertInjectedActivityNotFound(MANAGED_PROFILE_PKG);
286     }
287 
testNoSystemAppHasSyntheticAppDetailsActivityInjected()288     public void testNoSystemAppHasSyntheticAppDetailsActivityInjected() throws Exception {
289         List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(null, mUser);
290         for (LauncherActivityInfo activity : activities) {
291             if (!activity.getUser().equals(mUser)) {
292                 continue;
293             }
294             ApplicationInfo appInfo = activity.getApplicationInfo();
295             boolean isSystemApp = ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)
296                     || ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
297             if (isSystemApp) {
298                 // make sure we haven't generated a synthetic app details activity for it
299                 assertNotEquals("Found a system app that had a synthetic activity generated,"
300                         + " package name: " + activity.getComponentName().getPackageName()
301                         + "; activity name: " + activity.getName(),
302                         activity.getName(),
303                         SYNTHETIC_APP_DETAILS_ACTIVITY);
304             }
305         }
306     }
307 
expectSecurityException(ExceptionRunnable action, String failMessage)308     private void expectSecurityException(ExceptionRunnable action, String failMessage)
309             throws Exception {
310         try {
311             action.run();
312             fail(failMessage);
313         } catch (SecurityException e) {
314             // expected
315         }
316     }
317 
expectNameNotFoundException(ExceptionRunnable action, String failMessage)318     private void expectNameNotFoundException(ExceptionRunnable action, String failMessage)
319             throws Exception {
320         try {
321             action.run();
322             fail(failMessage);
323         } catch (PackageManager.NameNotFoundException e) {
324             // expected
325         }
326     }
327 
assertActivityInjected(String targetPackage)328     private void assertActivityInjected(String targetPackage) {
329         List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(null, mUser);
330         boolean noLaunchableActivityAppFound = false;
331         for (LauncherActivityInfo activity : activities) {
332             if (!activity.getUser().equals(mUser)) {
333                 continue;
334             }
335             ComponentName compName = activity.getComponentName();
336             if (compName.getPackageName().equals(targetPackage)) {
337                 noLaunchableActivityAppFound = true;
338                 // make sure it points to the synthetic app details activity
339                 assertEquals(activity.getName(), SYNTHETIC_APP_DETAILS_ACTIVITY);
340                 // make sure it's both exported and enabled
341                 try {
342                     PackageManager pm = mInstrumentation.getContext().getPackageManager();
343                     ActivityInfo ai = pm.getActivityInfo(compName, /*flags=*/ 0);
344                     assertTrue("Component " + compName + " is not enabled", ai.enabled);
345                     assertTrue("Component " + compName + " is not exported", ai.exported);
346                 } catch (NameNotFoundException e) {
347                     fail("Package " + compName.getPackageName() + " not found.");
348                 }
349             }
350         }
351         assertTrue(noLaunchableActivityAppFound);
352     }
353 
354     @FunctionalInterface
355     public interface ExceptionRunnable {
run()356         void run() throws Exception;
357     }
358 
getUserHandleArgument(UserManager userManager, String key, Bundle arguments)359     private UserHandle getUserHandleArgument(UserManager userManager, String key,
360             Bundle arguments) throws Exception {
361         String serial = arguments.getString(key);
362         if (serial == null) {
363             return null;
364         }
365         int serialNo = Integer.parseInt(serial);
366         return userManager.getUserForSerialNumber(serialNo);
367     }
368 
369     private class Connection implements ServiceConnection {
370         private final Semaphore mSemaphore = new Semaphore(0);
371 
372         @Override
onServiceConnected(ComponentName className, IBinder service)373         public void onServiceConnected(ComponentName className, IBinder service) {
374             mService = new Messenger(service);
375             mSemaphore.release();
376         }
377 
378         @Override
onServiceDisconnected(ComponentName className)379         public void onServiceDisconnected(ComponentName className) {
380             mService = null;
381         }
382 
waitForService()383         public void waitForService() {
384             try {
385                 if (mSemaphore.tryAcquire(5, TimeUnit.SECONDS)) {
386                     return;
387                 }
388             } catch (InterruptedException e) {
389             }
390             fail("failed to connec to service");
391         }
392     };
393 
394     private static class Result extends Handler {
395 
396         private final Semaphore mSemaphore = new Semaphore(0);
397         public int result = 0;
398 
Result(Looper looper)399         public Result(Looper looper) {
400             super(looper);
401         }
402 
403         @Override
handleMessage(Message msg)404         public void handleMessage(Message msg) {
405             if (msg.what == MSG_RESULT) {
406                 result = msg.arg1;
407                 mSemaphore.release();
408             } else {
409                 super.handleMessage(msg);
410             }
411         }
412 
waitForResult()413         public int waitForResult() {
414             try {
415                 if (mSemaphore.tryAcquire(120, TimeUnit.SECONDS)) {
416                      return result;
417                 }
418             } catch (InterruptedException e) {
419             }
420             return RESULT_TIMEOUT;
421         }
422     }
423 
424     public class ActivityLaunchedReceiver extends BroadcastReceiver {
425         public static final String ACTIVITY_LAUNCHED_ACTION =
426                 "com.android.cts.launchertests.LauncherAppsTests.LAUNCHED_ACTION";
427 
428         private final Semaphore mSemaphore = new Semaphore(0);
429 
430         @Override
onReceive(Context context, Intent intent)431         public void onReceive(Context context, Intent intent) {
432             if (intent.getAction().equals(ACTIVITY_LAUNCHED_ACTION)) {
433                 mSemaphore.release();
434             }
435         }
436 
waitForActivity()437         public int waitForActivity() {
438             try {
439                 if (mSemaphore.tryAcquire(5, TimeUnit.SECONDS)) {
440                     return RESULT_PASS;
441                 }
442             } catch (InterruptedException e) {
443             }
444             return RESULT_TIMEOUT;
445         }
446     }
447 
sendMessageToCallbacksService(int msg, UserHandle user, String packageName)448     private int sendMessageToCallbacksService(int msg, UserHandle user, String packageName)
449             throws Throwable {
450         Bundle params = new Bundle();
451         params.putParcelable(USER_EXTRA, user);
452         params.putString(PACKAGE_EXTRA, packageName);
453 
454         Message message = Message.obtain(null, msg, params);
455         message.replyTo = mResultMessenger;
456 
457         mService.send(message);
458 
459         return mResult.waitForResult();
460     }
461 
assertInjectedActivityNotFound(String targetPackage)462     private void assertInjectedActivityNotFound(String targetPackage) {
463         List<LauncherActivityInfo> activities = mLauncherApps.getActivityList(null, mUser);
464         for (LauncherActivityInfo activity : activities) {
465             if (!activity.getUser().equals(mUser)) {
466                 continue;
467             }
468             ComponentName compName = activity.getComponentName();
469             if (compName.getPackageName().equals(targetPackage)) {
470                 fail("Injected activity found: " + compName.flattenToString());
471             }
472         }
473     }
474 }
475