Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7380d873 authored by Julia Reynolds's avatar Julia Reynolds
Browse files

One time permission grant to default noti assistant

Test: runtest systemui-notification
Bug: 63095540
Change-Id: I5af42bddcf20b26ed5efcfa59dba3867624e4678
parent 200cd63f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -3243,4 +3243,7 @@
    <string name="config_fontFamilyButton">@string/font_family_button_material</string>

    <string translatable="false" name="config_batterySaverDeviceSpecificConfig"></string>

    <!-- Package name that should be granted Notification Assistant access -->
    <string name="config_defaultAssistantAccessPackage" translatable="false">android.ext.services</string>
</resources>
+2 −0
Original line number Diff line number Diff line
@@ -3212,4 +3212,6 @@
  <java-symbol type="string" name="harmful_app_warning_uninstall" />
  <java-symbol type="string" name="harmful_app_warning_launch_anyway" />
  <java-symbol type="string" name="harmful_app_warning_title" />

  <java-symbol type="string" name="config_defaultAssistantAccessPackage" />
</resources>
+18 −1
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@@ -98,6 +99,9 @@ abstract public class ManagedServices {
    static final String ATT_APPROVED_LIST = "approved";
    static final String ATT_USER_ID = "user";
    static final String ATT_IS_PRIMARY = "primary";
    static final String ATT_VERSION = "version";

    static final int DB_VERSION = 1;

    static final int APPROVAL_BY_PACKAGE = 0;
    static final int APPROVAL_BY_COMPONENT = 1;
@@ -295,6 +299,8 @@ abstract public class ManagedServices {
    public void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
        out.startTag(null, getConfig().xmlTag);

        out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));

        if (forBackup) {
            trimApprovedListsAccordingToInstalledServices();
        }
@@ -336,6 +342,14 @@ abstract public class ManagedServices {

    public void readXml(XmlPullParser parser)
            throws XmlPullParserException, IOException {
        // upgrade xml
        int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0);
        final List<UserInfo> activeUsers = mUm.getUsers(true);
        for (UserInfo userInfo : activeUsers) {
            upgradeXml(xmlVersion, userInfo.getUserHandle().getIdentifier());
        }

        // read grants
        int type;
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
            String tag = parser.getName();
@@ -346,6 +360,7 @@ abstract public class ManagedServices {
            if (type == XmlPullParser.START_TAG) {
                if (TAG_MANAGED_SERVICES.equals(tag)) {
                    Slog.i(TAG, "Read " + mConfig.caption + " permissions from xml");

                    final String approved = XmlUtils.readStringAttribute(parser, ATT_APPROVED_LIST);
                    final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
                    final boolean isPrimary =
@@ -360,6 +375,8 @@ abstract public class ManagedServices {
        rebindServices(false);
    }

    protected void upgradeXml(final int xmlVersion, final int userId) {}

    private void loadAllowedComponentsFromSettings() {

        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
@@ -379,7 +396,7 @@ abstract public class ManagedServices {
        Slog.d(TAG, "Done loading approved values from settings");
    }

    private void addApprovedList(String approved, int userId, boolean isPrimary) {
    protected void addApprovedList(String approved, int userId, boolean isPrimary) {
        if (TextUtils.isEmpty(approved)) {
            approved = "";
        }
+39 −4
Original line number Diff line number Diff line
@@ -434,6 +434,7 @@ public class NotificationManagerService extends SystemService {
                }
            }
        }

        String defaultDndAccess = getContext().getResources().getString(
                com.android.internal.R.string.config_defaultDndAccessPackages);
        if (defaultListenerAccess != null) {
@@ -446,6 +447,29 @@ public class NotificationManagerService extends SystemService {
                }
            }
        }

        readDefaultAssistant(userId);
    }

    protected void readDefaultAssistant(int userId) {
        String defaultAssistantAccess = getContext().getResources().getString(
                com.android.internal.R.string.config_defaultAssistantAccessPackage);
        if (defaultAssistantAccess != null) {
            // Gather all notification assistant components for candidate pkg. There should
            // only be one
            Set<ComponentName> approvedAssistants =
                    mAssistants.queryPackageForServices(defaultAssistantAccess,
                            PackageManager.MATCH_DIRECT_BOOT_AWARE
                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
            for (ComponentName cn : approvedAssistants) {
                try {
                    getBinderService().setNotificationAssistantAccessGrantedForUser(cn,
                            userId, true);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    void readPolicyXml(InputStream stream, boolean forRestore)
@@ -1155,6 +1179,7 @@ public class NotificationManagerService extends SystemService {
        }
    }

    @VisibleForTesting
    void clearNotifications() {
        mEnqueuedNotifications.clear();
        mNotificationList.clear();
@@ -1374,7 +1399,8 @@ public class NotificationManagerService extends SystemService {
                AppGlobals.getPackageManager(), getContext().getPackageManager(),
                getLocalService(LightsManager.class),
                new NotificationListeners(AppGlobals.getPackageManager()),
                new NotificationAssistants(AppGlobals.getPackageManager()),
                new NotificationAssistants(getContext(), mNotificationLock, mUserProfiles,
                        AppGlobals.getPackageManager()),
                new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
                null, snoozeHelper, new NotificationUsageStats(getContext()),
                new AtomicFile(new File(systemDir, "notification_policy.xml")),
@@ -5531,8 +5557,9 @@ public class NotificationManagerService extends SystemService {
    public class NotificationAssistants extends ManagedServices {
        static final String TAG_ENABLED_NOTIFICATION_ASSISTANTS = "enabled_assistants";

        public NotificationAssistants(IPackageManager pm) {
            super(getContext(), mNotificationLock, mUserProfiles, pm);
        public NotificationAssistants(Context context, Object lock, UserProfiles up,
                IPackageManager pm) {
            super(context, lock, up, pm);
        }

        @Override
@@ -5637,6 +5664,14 @@ public class NotificationManagerService extends SystemService {
        public boolean isEnabled() {
            return !getServices().isEmpty();
        }

        protected void upgradeXml(final int xmlVersion, final int userId) {
            if (xmlVersion == 0) {
                // one time approval of the OOB assistant
                Slog.d(TAG, "Approving default notification assistant for user " + userId);
                readDefaultAssistant(userId);
            }
        }
    }

    public class NotificationListeners extends ManagedServices {
@@ -6050,7 +6085,7 @@ public class NotificationManagerService extends SystemService {
        public static final String USAGE = "help\n"
                + "allow_listener COMPONENT [user_id]\n"
                + "disallow_listener COMPONENT [user_id]\n"
                + "set_assistant COMPONENT\n"
                + "allow_assistant COMPONENT\n"
                + "remove_assistant COMPONENT\n"
                + "allow_dnd PACKAGE\n"
                + "disallow_dnd PACKAGE";
+174 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.notification;

import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.content.ComponentName;
import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.os.UserManager;
import android.util.Slog;
import android.util.Xml;

import com.android.internal.util.FastXmlSerializer;
import com.android.server.UiServiceTestCase;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlSerializer;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.List;

public class NotificationAssistantsTest extends UiServiceTestCase {

    @Mock
    private PackageManager mPm;
    @Mock
    private IPackageManager miPm;
    @Mock
    private UserManager mUm;
    @Mock
    NotificationManagerService mNm;

    NotificationAssistants mAssistants;

    @Mock
    private ManagedServices.UserProfiles mUserProfiles;

    Object mLock = new Object();

    UserInfo mZero = new UserInfo(0, "zero", 0);
    UserInfo mTen = new UserInfo(10, "ten", 0);

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);

        getContext().setMockPackageManager(mPm);
        getContext().addMockSystemService(Context.USER_SERVICE, mUm);
        mAssistants = spy(mNm.new NotificationAssistants(getContext(), mLock, mUserProfiles, miPm));

        List<ResolveInfo> approved = new ArrayList<>();
        ResolveInfo resolve = new ResolveInfo();
        approved.add(resolve);
        ServiceInfo info = new ServiceInfo();
        info.packageName = "a";
        info.name="a";
        resolve.serviceInfo = info;
        when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
                .thenReturn(approved);

        List<UserInfo> users = new ArrayList<>();
        users.add(mZero);
        users.add(mTen);
        users.add(new UserInfo(11, "11", 0));
        users.add(new UserInfo(12, "12", 0));
        for (UserInfo user : users) {
            when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
        }
        when(mUm.getUsers()).thenReturn(users);
        when(mUm.getUsers(anyBoolean())).thenReturn(users);
        when(mUserProfiles.getCurrentProfileIds()).thenReturn(new int[] {0, 10, 11, 12});
    }

    @Test
    public void testXmlUpgrade() throws Exception {
        String xml = "<enabled_assistants/>";

        XmlPullParser parser = Xml.newPullParser();
        parser.setInput(new BufferedInputStream(
                new ByteArrayInputStream(xml.toString().getBytes())), null);
        parser.nextTag();
        mAssistants.readXml(parser);

        //once per user
        verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
    }

    @Test
    public void testXmlUpgradeExistingApprovedComponents() throws Exception {
        String xml = "<enabled_assistants>"
                + "<service_listing approved=\"b/b\" user=\"10\" primary=\"true\" />"
                + "</enabled_assistants>";

        XmlPullParser parser = Xml.newPullParser();
        parser.setInput(new BufferedInputStream(
                new ByteArrayInputStream(xml.toString().getBytes())), null);
        parser.nextTag();
        mAssistants.readXml(parser);

        // once per user
        verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());
        verify(mAssistants, times(1)).addApprovedList(
                new ComponentName("b", "b").flattenToString(),10, true);
    }

    @Test
    public void testXmlUpgradeOnce() throws Exception {
        String xml = "<enabled_assistants/>";

        XmlPullParser parser = Xml.newPullParser();
        parser.setInput(new BufferedInputStream(
                new ByteArrayInputStream(xml.toString().getBytes())), null);
        parser.nextTag();
        mAssistants.readXml(parser);

        XmlSerializer serializer = new FastXmlSerializer();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
        serializer.startDocument(null, true);
        mAssistants.writeXml(serializer, true);
        serializer.endDocument();
        serializer.flush();

        //once per user
        verify(mNm, times(mUm.getUsers().size())).readDefaultAssistant(anyInt());

        Mockito.reset(mNm);

        parser = Xml.newPullParser();
        parser.setInput(new BufferedInputStream(
                new ByteArrayInputStream(baos.toByteArray())), null);
        parser.nextTag();
        mAssistants.readXml(parser);

        //once per user
        verify(mNm, never()).readDefaultAssistant(anyInt());
    }
}