Home | History | Annotate | Download | only in launchertests
      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
     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 
    111     public void testGetActivitiesForUserFails() throws Exception {
    112         expectSecurityException(() -> mLauncherApps.getActivityList(null, mUser),
    113                 "getActivities for non-profile user failed to throw exception");
    114     }
    115 
    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 
    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 
    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 
    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 
    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 
    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     }
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    261     public void testProfileOwnerLauncherActivityInjected() throws Exception {
    262         assertActivityInjected(MANAGED_PROFILE_PKG);
    263     }
    264 
    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 
    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 
    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 
    284     public void testProfileOwnerInjectedActivityNotFound() throws Exception {
    285         assertInjectedActivityNotFound(MANAGED_PROFILE_PKG);
    286     }
    287 
    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 
    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 
    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 
    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 {
    356         void run() throws Exception;
    357     }
    358 
    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
    373         public void onServiceConnected(ComponentName className, IBinder service) {
    374             mService = new Messenger(service);
    375             mSemaphore.release();
    376         }
    377 
    378         @Override
    379         public void onServiceDisconnected(ComponentName className) {
    380             mService = null;
    381         }
    382 
    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 
    399         public Result(Looper looper) {
    400             super(looper);
    401         }
    402 
    403         @Override
    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 
    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
    431         public void onReceive(Context context, Intent intent) {
    432             if (intent.getAction().equals(ACTIVITY_LAUNCHED_ACTION)) {
    433                 mSemaphore.release();
    434             }
    435         }
    436 
    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 
    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 
    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