chromium/chrome/android/webapk/libs/runtime_library/javatests/src/org/chromium/webapk/lib/runtime_library/WebApkServiceImplTest.java

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.webapk.lib.runtime_library;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.IBinder;

import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.SmallTest;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.test.BaseJUnit4ClassRunner;
import org.chromium.base.test.util.CallbackHelper;

/** Instrumentation tests for {@link org.chromium.webapk.WebApkServiceImpl}. */
@RunWith(BaseJUnit4ClassRunner.class)
public class WebApkServiceImplTest {
    private static final String APK_WITH_WEBAPK_SERVICE_PACKAGE =
            "org.chromium.webapk.lib.runtime_library.test.apk_with_webapk_service";
    private static final String WEBAPK_SERVICE_IMPL_WRAPPER_CLASS_NAME =
            "org.chromium.webapk.lib.runtime_library.test.TestWebApkServiceImplWrapper";

    private static final int SMALL_ICON_ID = 1229;

    private Context mContext;
    private Context mTargetContext;

    /** The target app's uid. */
    private int mTargetUid;

    /** CallbackHelper which blocks till the service is connected. */
    private static class ServiceConnectionWaiter extends CallbackHelper
            implements ServiceConnection {
        private IWebApkApi mApi;

        public IWebApkApi api() {
            return mApi;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mApi = IWebApkApi.Stub.asInterface(service);
            notifyCalled();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {}
    }

    @Before
    public void setUp() {
        mContext = ApplicationProvider.getApplicationContext();
        mTargetContext = ApplicationProvider.getApplicationContext();
        mTargetUid = getUid(mTargetContext);
    }

    /** Test that an application which is not allowed to use the WebAPK service actually cannot. */
    @Test
    @SmallTest
    public void testApiFailsIfNoPermission() throws Exception {
        IWebApkApi api = bindService(mContext, mTargetUid + 1, SMALL_ICON_ID);
        try {
            // Check that the api either throws an exception or returns a default small icon id.
            int actualSmallIconId = api.getSmallIconId();
            Assert.assertTrue(actualSmallIconId != SMALL_ICON_ID);
        } catch (Exception e) {
        }
    }

    /** Test that an application which is allowed to use the WebAPK service actually can. */
    @Test
    @SmallTest
    public void testApiWorksIfHasPermission() throws Exception {
        IWebApkApi api = bindService(mContext, mTargetUid, SMALL_ICON_ID);
        try {
            // Check that the api returns the real small icon id.
            int actualSmallIconId = api.getSmallIconId();
            Assert.assertEquals(SMALL_ICON_ID, actualSmallIconId);
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail("Should not have thrown an exception when permission is granted.");
        }
    }

    /** Returns the uid for {@link context}. */
    private static int getUid(Context context) {
        PackageManager packageManager = context.getPackageManager();
        ApplicationInfo appInfo;
        try {
            appInfo =
                    packageManager.getApplicationInfo(
                            context.getPackageName(), PackageManager.GET_META_DATA);
            return appInfo.uid;
        } catch (Exception e) {
            Assert.fail();
        }
        return -1;
    }

    /**
     * Binds to the WebAPK service and blocks till the service is connected.
     *
     * @param context The context for the application containing the WebAPK service to bind to.
     * @param authorizedUid The uid of the only application allowed to use the WebAPK service's
     *     methods.
     * @param smallIconId The real small icon id.
     * @return IWebApkApi to use to communicate with the service.
     */
    private static IWebApkApi bindService(Context context, int authorizedUid, int smallIconId)
            throws Exception {
        Intent intent = new Intent();
        intent.setComponent(
                new ComponentName(
                        APK_WITH_WEBAPK_SERVICE_PACKAGE, WEBAPK_SERVICE_IMPL_WRAPPER_CLASS_NAME));
        intent.putExtra(WebApkServiceImpl.KEY_SMALL_ICON_ID, smallIconId);
        intent.putExtra(WebApkServiceImpl.KEY_HOST_BROWSER_UID, authorizedUid);

        ServiceConnectionWaiter waiter = new ServiceConnectionWaiter();
        context.bindService(intent, waiter, Context.BIND_AUTO_CREATE);
        waiter.waitForCallback(0);

        IWebApkApi api = waiter.api();
        Assert.assertNotNull(api);
        return api;
    }
}