chromium/chrome/android/webapk/libs/runtime_library/src/org/chromium/webapk/lib/runtime_library/WebApkServiceImpl.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.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;

/** Implements services offered by the WebAPK to Chrome. */
public class WebApkServiceImpl extends IWebApkApi.Stub {

    public static final String KEY_SMALL_ICON_ID = "small_icon_id";
    public static final String KEY_HOST_BROWSER_UID = "host_browser_uid";

    private static final String TAG = "WebApkServiceImpl";

    private final Context mContext;

    /** Id of icon to represent WebAPK notifications in status bar. */
    private final int mSmallIconId;

    /**
     * Uid of only application allowed to call the service's methods. If an application with a
     * different uid calls the service, the service throws a RemoteException.
     */
    private final int mHostUid;

    /**
     * Creates an instance of WebApkServiceImpl.
     *
     * @param context
     * @param bundle Bundle with additional constructor parameters.
     */
    public WebApkServiceImpl(Context context, Bundle bundle) {
        mContext = context;
        mSmallIconId = bundle.getInt(KEY_SMALL_ICON_ID);
        mHostUid = bundle.getInt(KEY_HOST_BROWSER_UID);
        assert mHostUid >= 0;
    }

    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        int callingUid = Binder.getCallingUid();
        if (mHostUid != callingUid) {
            throw new RemoteException(
                    "Unauthorized caller "
                            + callingUid
                            + " does not match expected host="
                            + mHostUid);
        }
        return super.onTransact(code, data, reply, flags);
    }

    @Override
    public int getSmallIconId() {
        return mSmallIconId;
    }

    @Override
    public void notifyNotification(String platformTag, int platformID, Notification notification) {
        Log.w(
                TAG,
                "Should NOT reach WebApkServiceImpl#notifyNotification(String, int,"
                        + " Notification).");
    }

    @Override
    public void cancelNotification(String platformTag, int platformID) {
        getNotificationManager().cancel(platformTag, platformID);
    }

    @Override
    public boolean notificationPermissionEnabled() {
        Log.w(
                TAG,
                "Should NOT reach WebApkServiceImpl#notificationPermissionEnabled() because it is"
                        + " deprecated.");
        NotificationManager notificationManager =
                (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
        return notificationManager.areNotificationsEnabled();
    }

    @SuppressLint("NewApi")
    @Override
    public boolean finishAndRemoveTaskSdk23() {
        ActivityManager manager =
                (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        String webApkPackageName = mContext.getPackageName();
        for (ActivityManager.AppTask task : manager.getAppTasks()) {
            if (TextUtils.equals(getTaskBaseActivityPackageName(task), webApkPackageName)) {
                task.finishAndRemoveTask();
                return true;
            }
        }
        return false;
    }

    @Override
    public int checkNotificationPermission() {
        Log.w(TAG, "Should NOT reach WebApkServiceImpl#checkNotificationPermission().");
        return -1;
    }

    @Override
    public PendingIntent requestNotificationPermission(String channelName, String channelId) {
        Log.w(
                TAG,
                "Should NOT reach WebApkServiceImpl#requestNotificationPermission(String,"
                        + " String).");
        return null;
    }

    /** Returns the package name of the task's base activity. */
    private static String getTaskBaseActivityPackageName(ActivityManager.AppTask task) {
        try {
            ActivityManager.RecentTaskInfo info = task.getTaskInfo();
            if (info != null && info.baseActivity != null) {
                return info.baseActivity.getPackageName();
            }
        } catch (IllegalArgumentException e) {
        }
        return null;
    }

    @SuppressWarnings("NewApi")
    @Override
    public void notifyNotificationWithChannel(
            String platformTag, int platformID, Notification notification, String channelName) {
        NotificationManager notificationManager = getNotificationManager();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && notification.getChannelId() != null) {
            NotificationChannel channel =
                    new NotificationChannel(
                            notification.getChannelId(),
                            channelName,
                            NotificationManager.IMPORTANCE_DEFAULT);
            notificationManager.createNotificationChannel(channel);
        }

        notificationManager.notify(platformTag, platformID, notification);
    }

    private NotificationManager getNotificationManager() {
        return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    }
}