Merge "NAS Settings Migration" into sc-dev

This commit is contained in:
Chloris Kuo
2021-04-12 17:16:57 +00:00
committed by Android (Google) Code Review
12 changed files with 677 additions and 9 deletions

View File

@@ -188,6 +188,8 @@ interface INotificationManager
List<ComponentName> getEnabledNotificationListeners(int userId);
ComponentName getAllowedNotificationAssistantForUser(int userId);
ComponentName getAllowedNotificationAssistant();
ComponentName getDefaultNotificationAssistant();
void resetDefaultNotificationAssistant(boolean loadFromConfig);
boolean hasEnabledNotificationListener(String packageName, int userId);
@UnsupportedAppUsage

View File

@@ -9928,6 +9928,14 @@ public final class Settings {
public static final String CLIPBOARD_SHOW_ACCESS_NOTIFICATIONS =
"clipboard_show_access_notifications";
/**
* If nonzero, nas has not been updated to reflect new changes.
* @hide
*/
@Readable
public static final String NAS_SETTINGS_UPDATED = "nas_settings_updated";
/**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.

View File

@@ -5982,6 +5982,12 @@
android:exported="false">
</activity>
<activity android:name="com.android.server.notification.NASLearnMoreActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
android:exported="false">
</activity>
<receiver android:name="com.android.server.BootReceiver"
android:exported="true"
android:systemUserOnly="true">

View File

@@ -5513,6 +5513,21 @@
<!-- Content description of the demoted feedback icon in the notification. [CHAR LIMIT=NONE] -->
<string name="notification_feedback_indicator_demoted">This notification was ranked lower. Tap to provide feedback.</string>
<!-- Notification Intelligence -->
<!-- Title of notification indicating notification intelligence settings have changed when upgrading to S [CHAR LIMIT=30] -->
<string name="nas_upgrade_notification_title">Try enhanced notifications</string>
<!-- Content of notification indicating users need to update the settings [CHAR LIMIT=NONE] -->
<string name="nas_upgrade_notification_content">To keep getting suggested actions, replies, and more, turn on enhanced notifications. Android Adaptive Notifications are no longer supported.</string>
<!-- Label of notification action button to turn on the notification intelligence [CHAR LIMIT=20] -->
<string name="nas_upgrade_notification_enable_action">Turn on</string>
<!-- Label of notification action button to turn off the notification intelligence [CHAR LIMIT=20] -->
<string name="nas_upgrade_notification_disable_action">Not now</string>
<!-- Label of notification action button to learn more about the notification intelligence settings [CHAR LIMIT=20] -->
<string name="nas_upgrade_notification_learn_more_action">Learn more</string>
<!-- Content of notification learn more dialog about the notification intelligence settings [CHAR LIMIT=NONE] -->
<string name="nas_upgrade_notification_learn_more_content">Enhanced notifications can read all notification content, including personal information like contact names and messages. This feature can also dismiss notifications or take actions on buttons in notifications, such as answering phone calls.\n\nThis feature can also turn Priority mode on or off and change related settings.</string>
<!-- Dynamic mode battery saver strings -->
<!-- The user visible name of the notification channel for the routine mode battery saver fyi notification [CHAR_LIMIT=80]-->
<string name="dynamic_mode_notification_channel_name">Routine Mode info notification</string>

View File

@@ -2263,6 +2263,12 @@
<java-symbol type="style" name="Animation.RecentApplications" />
<java-symbol type="integer" name="dock_enter_exit_duration" />
<java-symbol type="bool" name="config_battery_percentage_setting_available" />
<java-symbol type="string" name="nas_upgrade_notification_title" />
<java-symbol type="string" name="nas_upgrade_notification_content" />
<java-symbol type="string" name="nas_upgrade_notification_enable_action" />
<java-symbol type="string" name="nas_upgrade_notification_disable_action" />
<java-symbol type="string" name="nas_upgrade_notification_learn_more_action" />
<java-symbol type="string" name="nas_upgrade_notification_learn_more_content" />
<!-- ImfTest -->
<java-symbol type="layout" name="auto_complete_list" />

View File

@@ -264,6 +264,10 @@ message SystemMessage {
// Package: android
NOTE_CARRIER_SUGGESTION_AVAILABLE = 63;
// Inform that NAS settings have changed on OS upgrade
// Package: android
NOTE_NAS_UPGRADE = 64;
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release

View File

@@ -600,11 +600,10 @@ abstract public class ManagedServices {
throws XmlPullParserException, IOException {
// read grants
int type;
String version = "";
String version = XmlUtils.readStringAttribute(parser, ATT_VERSION);
readDefaults(parser);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
String tag = parser.getName();
version = XmlUtils.readStringAttribute(parser, ATT_VERSION);
if (type == XmlPullParser.END_TAG
&& getConfig().xmlTag.equals(tag)) {
break;
@@ -642,7 +641,7 @@ abstract public class ManagedServices {
rebindServices(false, USER_ALL);
}
private void upgradeDefaultsXmlVersion() {
void upgradeDefaultsXmlVersion() {
// check if any defaults are loaded
int defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
if (defaultsSize == 0) {

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2021 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 android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.WindowManager;
import com.android.internal.R;
public class NASLearnMoreActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showLearnMoreDialog();
}
private void showLearnMoreDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
AlertDialog alertDialog = builder.setMessage(
R.string.nas_upgrade_notification_learn_more_content)
.setPositiveButton(R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
NASLearnMoreActivity.this.finish();
}
})
.create();
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
alertDialog.show();
}
}

View File

@@ -265,6 +265,7 @@ import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.messages.nano.SystemMessageProto;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.SomeArgs;
@@ -619,6 +620,12 @@ public class NotificationManagerService extends SystemService {
private NotificationRecordLogger mNotificationRecordLogger;
private InstanceIdSequence mNotificationInstanceIdSequence;
private Set<String> mMsgPkgsAllowedAsConvos = new HashSet();
protected static final String ACTION_ENABLE_NAS =
"android.server.notification.action.ENABLE_NAS";
protected static final String ACTION_DISABLE_NAS =
"android.server.notification.action.DISABLE_NAS";
protected static final String ACTION_LEARNMORE_NAS =
"android.server.notification.action.LEARNMORE_NAS";
static class Archive {
final SparseArray<Boolean> mEnabled;
@@ -725,6 +732,105 @@ public class NotificationManagerService extends SystemService {
setDefaultAssistantForUser(userId);
}
protected void migrateDefaultNASShowNotificationIfNecessary() {
final List<UserInfo> activeUsers = mUm.getUsers();
for (UserInfo userInfo : activeUsers) {
int userId = userInfo.getUserHandle().getIdentifier();
if (isNASMigrationDone(userId)) {
continue;
}
if (mAssistants.hasUserSet(userId)) {
mAssistants.loadDefaultsFromConfig(false);
ComponentName defaultFromConfig = mAssistants.getDefaultFromConfig();
List<ComponentName> allowedComponents = mAssistants.getAllowedComponents(userId);
if (allowedComponents.contains(defaultFromConfig)) {
setNASMigrationDone(userId);
mAssistants.resetDefaultFromConfig();
continue;
}
//User selected different NAS or none, need onboarding
enqueueNotificationInternal(getContext().getPackageName(),
getContext().getOpPackageName(), Binder.getCallingUid(),
Binder.getCallingPid(), TAG,
SystemMessageProto.SystemMessage.NOTE_NAS_UPGRADE,
createNASUpgradeNotification(userId), userId);
}
}
}
protected Notification createNASUpgradeNotification(int userId) {
final Bundle extras = new Bundle();
extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
getContext().getResources().getString(R.string.global_action_settings));
int title = R.string.nas_upgrade_notification_title;
int content = R.string.nas_upgrade_notification_content;
Intent onboardingIntent = new Intent(Settings.ACTION_NOTIFICATION_ASSISTANT_SETTINGS);
onboardingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
Intent enableIntent = new Intent(ACTION_ENABLE_NAS);
enableIntent.putExtra(Intent.EXTRA_USER_ID, userId);
PendingIntent enableNASPendingIntent = PendingIntent.getBroadcast(getContext(),
0, enableIntent, PendingIntent.FLAG_IMMUTABLE);
Intent disableIntent = new Intent(ACTION_DISABLE_NAS);
disableIntent.putExtra(Intent.EXTRA_USER_ID, userId);
PendingIntent disableNASPendingIntent = PendingIntent.getBroadcast(getContext(),
0, disableIntent, PendingIntent.FLAG_IMMUTABLE);
Intent learnMoreIntent = new Intent(ACTION_LEARNMORE_NAS);
learnMoreIntent.putExtra(Intent.EXTRA_USER_ID, userId);
PendingIntent learnNASPendingIntent = PendingIntent.getBroadcast(getContext(),
0, learnMoreIntent, PendingIntent.FLAG_IMMUTABLE);
Notification.Action enableNASAction = new Notification.Action.Builder(
0,
getContext().getResources().getString(
R.string.nas_upgrade_notification_enable_action),
enableNASPendingIntent).build();
Notification.Action disableNASAction = new Notification.Action.Builder(
0,
getContext().getResources().getString(
R.string.nas_upgrade_notification_disable_action),
disableNASPendingIntent).build();
Notification.Action learnMoreNASAction = new Notification.Action.Builder(
0,
getContext().getResources().getString(
R.string.nas_upgrade_notification_learn_more_action),
learnNASPendingIntent).build();
return new Notification.Builder(getContext(), SystemNotificationChannels.ALERTS)
.setAutoCancel(false)
.setOngoing(true)
.setTicker(getContext().getResources().getString(title))
.setSmallIcon(R.drawable.ic_settings_24dp)
.setContentTitle(getContext().getResources().getString(title))
.setContentText(getContext().getResources().getString(content))
.setContentIntent(PendingIntent.getActivity(getContext(), 0, onboardingIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
.setLocalOnly(true)
.setStyle(new Notification.BigTextStyle())
.addAction(enableNASAction)
.addAction(disableNASAction)
.addAction(learnMoreNASAction)
.build();
}
@VisibleForTesting
void setNASMigrationDone(int userId) {
Settings.Secure.putIntForUser(getContext().getContentResolver(),
Settings.Secure.NAS_SETTINGS_UPDATED, 1, userId);
}
@VisibleForTesting
boolean isNASMigrationDone(int userId) {
return (Settings.Secure.getIntForUser(getContext().getContentResolver(),
Settings.Secure.NAS_SETTINGS_UPDATED, 0, userId) == 1);
}
protected void setDefaultAssistantForUser(int userId) {
String overrideDefaultAssistantString = DeviceConfig.getProperty(
DeviceConfig.NAMESPACE_SYSTEMUI,
@@ -1740,6 +1846,41 @@ public class NotificationManagerService extends SystemService {
}
};
private final BroadcastReceiver mNASIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
if (ACTION_ENABLE_NAS.equals(action)) {
mAssistants.resetDefaultFromConfig();
setNotificationAssistantAccessGrantedForUserInternal(
CollectionUtils.firstOrNull(mAssistants.getDefaultComponents()),
userId, true, true);
setNASMigrationDone(userId);
cancelNotificationInternal(getContext().getPackageName(),
getContext().getOpPackageName(), Binder.getCallingUid(),
Binder.getCallingPid(), TAG,
SystemMessageProto.SystemMessage.NOTE_NAS_UPGRADE, userId);
} else if (ACTION_DISABLE_NAS.equals(action)) {
//Set default NAS to be null if user selected none during migration
mAssistants.clearDefaults();
setNotificationAssistantAccessGrantedForUserInternal(
null, userId, true, true);
setNASMigrationDone(userId);
cancelNotificationInternal(getContext().getPackageName(),
getContext().getOpPackageName(), Binder.getCallingUid(),
Binder.getCallingPid(), TAG,
SystemMessageProto.SystemMessage.NOTE_NAS_UPGRADE, userId);
} else if (ACTION_LEARNMORE_NAS.equals(action)) {
Intent i = new Intent(getContext(), NASLearnMoreActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getContext().sendBroadcastAsUser(
new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), UserHandle.of(userId));
getContext().startActivity(i);
}
}
};
private final class SettingsObserver extends ContentObserver {
private final Uri NOTIFICATION_BADGING_URI
= Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_BADGING);
@@ -2263,6 +2404,12 @@ public class NotificationManagerService extends SystemService {
IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
getContext().registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
IntentFilter nasFilter = new IntentFilter();
nasFilter.addAction(ACTION_ENABLE_NAS);
nasFilter.addAction(ACTION_DISABLE_NAS);
nasFilter.addAction(ACTION_LEARNMORE_NAS);
getContext().registerReceiver(mNASIntentReceiver, nasFilter);
}
/**
@@ -2274,6 +2421,7 @@ public class NotificationManagerService extends SystemService {
getContext().unregisterReceiver(mNotificationTimeoutReceiver);
getContext().unregisterReceiver(mRestoreReceiver);
getContext().unregisterReceiver(mLocaleChangeReceiver);
getContext().unregisterReceiver(mNASIntentReceiver);
if (mDeviceConfigChangedListener != null) {
DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
@@ -2529,6 +2677,7 @@ public class NotificationManagerService extends SystemService {
mConditionProviders.onBootPhaseAppsCanStart();
mHistoryManager.onBootPhaseAppsCanStart();
registerDeviceConfigChange();
migrateDefaultNASShowNotificationIfNecessary();
} else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mSnoozeHelper.scheduleRepostsForPersistedNotifications(System.currentTimeMillis());
}
@@ -5020,6 +5169,28 @@ public class NotificationManagerService extends SystemService {
return getAllowedNotificationAssistantForUser(getCallingUserHandle().getIdentifier());
}
@Override
public ComponentName getDefaultNotificationAssistant() {
checkCallerIsSystem();
ArraySet<ComponentName> defaultComponents = mAssistants.getDefaultComponents();
if (defaultComponents.size() > 1) {
Slog.w(TAG, "More than one default NotificationAssistant: "
+ defaultComponents.size());
}
return CollectionUtils.firstOrNull(defaultComponents);
}
@Override
public void resetDefaultNotificationAssistant(boolean loadFromConfig) {
checkCallerIsSystem();
if (loadFromConfig) {
mAssistants.resetDefaultFromConfig();
} else {
mAssistants.clearDefaults();
}
}
@Override
public boolean hasEnabledNotificationListener(String packageName, int userId) {
checkCallerIsSystem();
@@ -9209,8 +9380,14 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mLock")
private Set<String> mAllowedAdjustments = new ArraySet<>();
protected ComponentName mDefaultFromConfig = null;
@Override
protected void loadDefaultsFromConfig() {
loadDefaultsFromConfig(true);
}
protected void loadDefaultsFromConfig(boolean addToDefault) {
ArraySet<String> assistants = new ArraySet<>();
assistants.addAll(Arrays.asList(mContext.getResources().getString(
com.android.internal.R.string.config_defaultAssistantAccessComponent)
@@ -9228,11 +9405,22 @@ public class NotificationManagerService extends SystemService {
ArraySet<ComponentName> approved = queryPackageForServices(packageName,
MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, USER_SYSTEM);
if (approved.contains(assistantCn)) {
addDefaultComponentOrPackage(assistantCn.flattenToString());
if (addToDefault) {
// add the default loaded from config file to mDefaultComponents and
// mDefaultPackages
addDefaultComponentOrPackage(assistantCn.flattenToString());
} else {
// otherwise, store in the mDefaultFromConfig for NAS settings migration
mDefaultFromConfig = assistantCn;
}
}
}
}
ComponentName getDefaultFromConfig() {
return mDefaultFromConfig;
}
public NotificationAssistants(Context context, Object lock, UserProfiles up,
IPackageManager pm) {
super(context, lock, up, pm);
@@ -9714,12 +9902,26 @@ public class NotificationManagerService extends SystemService {
for (UserInfo userInfo : activeUsers) {
int userId = userInfo.getUserHandle().getIdentifier();
if (!hasUserSet(userId)) {
if (!isNASMigrationDone(userId)) {
resetDefaultFromConfig();
setNASMigrationDone(userId);
}
Slog.d(TAG, "Approving default notification assistant for user " + userId);
setDefaultAssistantForUser(userId);
}
}
}
protected void resetDefaultFromConfig() {
clearDefaults();
loadDefaultsFromConfig();
}
protected void clearDefaults() {
mDefaultComponents.clear();
mDefaultPackages.clear();
}
@Override
protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId,
boolean isPrimary, boolean enabled, boolean userSet) {

View File

@@ -1416,6 +1416,18 @@ public class ManagedServicesTest extends UiServiceTestCase {
new ArraySet(Arrays.asList(new ComponentName("xml", "class"))));
}
@Test
public void loadDefaults_versionLatest_NoLoadDefaults() throws Exception {
resetComponentsAndPackages();
mDefaults.add(new ComponentName("default", "class"));
mDefaultsString = "xml/class";
mVersionString = String.valueOf(mService.DB_VERSION);
loadXml(mService);
assertEquals(mService.getDefaultComponents(),
new ArraySet(Arrays.asList(new ComponentName("xml", "class"))));
}
private void resetComponentsAndPackages() {
ArrayMap<Integer, ArrayMap<Integer, String>> empty = new ArrayMap(1);
ArrayMap<Integer, String> emptyPkgs = new ArrayMap(0);

View File

@@ -18,10 +18,12 @@ package com.android.server.notification;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertNull;
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.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -38,6 +40,8 @@ import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.testing.TestableContext;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.TypedXmlPullParser;
import android.util.Xml;
@@ -53,6 +57,7 @@ import org.mockito.MockitoAnnotations;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class NotificationAssistantsTest extends UiServiceTestCase {
@@ -73,6 +78,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
@Mock
private ManagedServices.UserProfiles mUserProfiles;
private TestableContext mContext = spy(getContext());
Object mLock = new Object();
@@ -82,10 +88,11 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
@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));
mContext.setMockPackageManager(mPm);
mContext.addMockSystemService(Context.USER_SERVICE, mUm);
mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm));
when(mNm.getBinderService()).thenReturn(mINm);
mContext.ensureTestableResources();
List<ResolveInfo> approved = new ArrayList<>();
ResolveInfo resolve = new ResolveInfo();
@@ -113,6 +120,7 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
profileIds.add(10);
profileIds.add(12);
when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
when(mNm.isNASMigrationDone(anyInt())).thenReturn(true);
}
@Test
@@ -189,4 +197,122 @@ public class NotificationAssistantsTest extends UiServiceTestCase {
verify(mNm, never()).setNotificationAssistantAccessGrantedForUserInternal(
any(ComponentName.class), eq(mZero.id), anyBoolean(), anyBoolean());
}
@Test
public void testLoadDefaultsFromConfig() {
ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
ComponentName newDefaultComponent = ComponentName.unflattenFromString("package/Component2");
doReturn(new ArraySet<>(Arrays.asList(oldDefaultComponent, newDefaultComponent)))
.when(mAssistants).queryPackageForServices(any(), anyInt(), anyInt());
// Test loadDefaultsFromConfig() add the config value to mDefaultComponents instead of
// mDefaultFromConfig
when(mContext.getResources().getString(
com.android.internal.R.string.config_defaultAssistantAccessComponent))
.thenReturn(oldDefaultComponent.flattenToString());
mAssistants.loadDefaultsFromConfig();
assertEquals(new ArraySet<>(Arrays.asList(oldDefaultComponent)),
mAssistants.getDefaultComponents());
assertNull(mAssistants.getDefaultFromConfig());
// Test loadDefaultFromConfig(false) only updates the mDefaultFromConfig
when(mContext.getResources().getString(
com.android.internal.R.string.config_defaultAssistantAccessComponent))
.thenReturn(newDefaultComponent.flattenToString());
mAssistants.loadDefaultsFromConfig(false);
assertEquals(new ArraySet<>(Arrays.asList(oldDefaultComponent)),
mAssistants.getDefaultComponents());
assertEquals(newDefaultComponent, mAssistants.getDefaultFromConfig());
// Test resetDefaultFromConfig updates the mDefaultComponents to new config value
mAssistants.resetDefaultFromConfig();
assertEquals(new ArraySet<>(Arrays.asList(newDefaultComponent)),
mAssistants.getDefaultComponents());
assertEquals(newDefaultComponent, mAssistants.getDefaultFromConfig());
}
@Test
public void testNASSettingUpgrade_userNotSet_differentDefaultNAS() {
ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
ComponentName newDefaultComponent = ComponentName.unflattenFromString("package/Component2");
when(mNm.isNASMigrationDone(anyInt())).thenReturn(false);
doReturn(new ArraySet<>(Arrays.asList(newDefaultComponent)))
.when(mAssistants).queryPackageForServices(any(), anyInt(), anyInt());
when(mContext.getResources().getString(
com.android.internal.R.string.config_defaultAssistantAccessComponent))
.thenReturn(newDefaultComponent.flattenToString());
// User hasn't set the default NAS, set the oldNAS as the default with userSet=false here.
mAssistants.setPackageOrComponentEnabled(oldDefaultComponent.flattenToString(),
mZero.id, true, true /*enabled*/, false /*userSet*/);
// The migration for userSet==false happens in resetDefaultAssistantsIfNecessary
mAssistants.resetDefaultAssistantsIfNecessary();
// Verify the migration happened: setDefaultAssistantForUser should be called to
// update defaults
verify(mNm, times(1)).setNASMigrationDone(eq(mZero.id));
verify(mNm, times(1)).setDefaultAssistantForUser(eq(mZero.id));
assertEquals(new ArraySet<>(Arrays.asList(newDefaultComponent)),
mAssistants.getDefaultComponents());
when(mNm.isNASMigrationDone(anyInt())).thenReturn(true);
// Test resetDefaultAssistantsIfNecessary again since it will be called on every reboot
mAssistants.resetDefaultAssistantsIfNecessary();
// The migration should not happen again, the invoke time for migration should not increase
verify(mNm, times(1)).setNASMigrationDone(eq(mZero.id));
// The invoke time outside migration part should increase by 1
verify(mNm, times(2)).setDefaultAssistantForUser(eq(mZero.id));
}
@Test
public void testNASSettingUpgrade_userNotSet_sameDefaultNAS() {
ComponentName defaultComponent = ComponentName.unflattenFromString("package/Component1");
when(mNm.isNASMigrationDone(anyInt())).thenReturn(false);
doReturn(new ArraySet<>(Arrays.asList(defaultComponent)))
.when(mAssistants).queryPackageForServices(any(), anyInt(), anyInt());
when(mContext.getResources().getString(
com.android.internal.R.string.config_defaultAssistantAccessComponent))
.thenReturn(defaultComponent.flattenToString());
// User hasn't set the default NAS, set the oldNAS as the default with userSet=false here.
mAssistants.setPackageOrComponentEnabled(defaultComponent.flattenToString(),
mZero.id, true, true /*enabled*/, false /*userSet*/);
// The migration for userSet==false happens in resetDefaultAssistantsIfNecessary
mAssistants.resetDefaultAssistantsIfNecessary();
verify(mNm, times(1)).setNASMigrationDone(eq(mZero.id));
verify(mNm, times(1)).setDefaultAssistantForUser(eq(mZero.id));
assertEquals(new ArraySet<>(Arrays.asList(defaultComponent)),
mAssistants.getDefaultComponents());
}
@Test
public void testNASSettingUpgrade_userNotSet_defaultNASNone() {
ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
when(mNm.isNASMigrationDone(anyInt())).thenReturn(false);
doReturn(new ArraySet<>(Arrays.asList(oldDefaultComponent)))
.when(mAssistants).queryPackageForServices(any(), anyInt(), anyInt());
// New default is none
when(mContext.getResources().getString(
com.android.internal.R.string.config_defaultAssistantAccessComponent))
.thenReturn("");
// User hasn't set the default NAS, set the oldNAS as the default with userSet=false here.
mAssistants.setPackageOrComponentEnabled(oldDefaultComponent.flattenToString(),
mZero.id, true, true /*enabled*/, false /*userSet*/);
// The migration for userSet==false happens in resetDefaultAssistantsIfNecessary
mAssistants.resetDefaultAssistantsIfNecessary();
verify(mNm, times(1)).setNASMigrationDone(eq(mZero.id));
verify(mNm, times(1)).setDefaultAssistantForUser(eq(mZero.id));
assertEquals(new ArraySet<>(), mAssistants.getDefaultComponents());
}
}

View File

@@ -58,10 +58,13 @@ import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
import static com.android.server.notification.NotificationManagerService.ACTION_DISABLE_NAS;
import static com.android.server.notification.NotificationManagerService.ACTION_ENABLE_NAS;
import static com.android.server.notification.NotificationManagerService.ACTION_LEARNMORE_NAS;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -313,6 +316,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
@Mock
MultiRateLimiter mToastRateLimiter;
BroadcastReceiver mPackageIntentReceiver;
BroadcastReceiver mNASIntentReceiver;
NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
1 << 30);
@@ -526,6 +530,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(),
any(), intentFilterCaptor.capture(), any(), any());
verify(mContext, atLeastOnce()).registerReceiver(broadcastReceiverCaptor.capture(),
intentFilterCaptor.capture());
List<BroadcastReceiver> broadcastReceivers = broadcastReceiverCaptor.getAllValues();
List<IntentFilter> intentFilters = intentFilterCaptor.getAllValues();
@@ -535,10 +541,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
&& filter.hasAction(Intent.ACTION_PACKAGES_UNSUSPENDED)
&& filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) {
mPackageIntentReceiver = broadcastReceivers.get(i);
break;
} else if (filter.hasAction(ACTION_ENABLE_NAS)
&& filter.hasAction(ACTION_DISABLE_NAS)
&& filter.hasAction(ACTION_LEARNMORE_NAS)) {
mNASIntentReceiver = broadcastReceivers.get(i);
}
}
assertNotNull("package intent receiver should exist", mPackageIntentReceiver);
assertNotNull("nas intent receiver should exist", mNASIntentReceiver);
// Pretend the shortcut exists
List<ShortcutInfo> shortcutInfos = new ArrayList<>();
@@ -632,6 +642,16 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mPackageIntentReceiver.onReceive(getContext(), intent);
}
private void simulateNASUpgradeBroadcast(String action, int uid) {
final Bundle extras = new Bundle();
extras.putInt(Intent.EXTRA_USER_ID, uid);
final Intent intent = new Intent(action);
intent.putExtras(extras);
mNASIntentReceiver.onReceive(getContext(), intent);
}
private ArrayMap<Boolean, ArrayList<ComponentName>> generateResetComponentValues() {
ArrayMap<Boolean, ArrayList<ComponentName>> changed = new ArrayMap<>();
changed.put(true, new ArrayList<>());
@@ -5729,6 +5749,223 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
eq("device"), eq(0), eq(false), eq(true));
}
@Test
public void testNASSettingUpgrade_userSetNull_showOnBoarding() throws RemoteException {
ComponentName newDefaultComponent = ComponentName.unflattenFromString("package/Component1");
TestableNotificationManagerService service = spy(mService);
int userId = 11;
setUsers(new int[]{userId});
setNASMigrationDone(false, userId);
when(mAssistants.getDefaultFromConfig())
.thenReturn(newDefaultComponent);
when(mAssistants.getAllowedComponents(anyInt()))
.thenReturn(new ArrayList<>());
when(mAssistants.hasUserSet(userId)).thenReturn(true);
service.migrateDefaultNASShowNotificationIfNecessary();
assertFalse(service.isNASMigrationDone(userId));
verify(service, times(1)).createNASUpgradeNotification(eq(userId));
verify(mAssistants, times(0)).resetDefaultFromConfig();
//Test user clear data before enable/disable from onboarding notification
ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners =
generateResetComponentValues();
when(mListeners.resetComponents(anyString(), anyInt())).thenReturn(changedListeners);
ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>();
changes.put(true, new ArrayList(Arrays.asList(newDefaultComponent)));
changes.put(false, new ArrayList());
when(mAssistants.resetComponents(anyString(), anyInt())).thenReturn(changes);
//Clear data
service.getBinderService().clearData("package", userId, false);
//Test migrate flow again
service.migrateDefaultNASShowNotificationIfNecessary();
//The notification should be still there
assertFalse(service.isNASMigrationDone(userId));
verify(service, times(2)).createNASUpgradeNotification(eq(userId));
verify(mAssistants, times(0)).resetDefaultFromConfig();
assertEquals(null, service.getApprovedAssistant(userId));
}
@Test
public void testNASSettingUpgrade_userSetDifferentDefault_showOnboarding()
throws RemoteException {
ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
ComponentName newDefaultComponent = ComponentName.unflattenFromString("package/Component2");
TestableNotificationManagerService service = spy(mService);
int userId = 11;
setUsers(new int[]{userId});
setNASMigrationDone(false, userId);
when(mAssistants.getDefaultComponents())
.thenReturn(new ArraySet<>(Arrays.asList(oldDefaultComponent)));
when(mAssistants.getDefaultFromConfig())
.thenReturn(newDefaultComponent);
when(mAssistants.getAllowedComponents(anyInt()))
.thenReturn(Arrays.asList(oldDefaultComponent));
when(mAssistants.hasUserSet(userId)).thenReturn(true);
service.migrateDefaultNASShowNotificationIfNecessary();
assertFalse(service.isNASMigrationDone(userId));
verify(service, times(1)).createNASUpgradeNotification(eq(userId));
verify(mAssistants, times(0)).resetDefaultFromConfig();
//Test user clear data before enable/disable from onboarding notification
ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners =
generateResetComponentValues();
when(mListeners.resetComponents(anyString(), anyInt())).thenReturn(changedListeners);
ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>();
changes.put(true, new ArrayList(Arrays.asList(newDefaultComponent)));
changes.put(false, new ArrayList());
when(mAssistants.resetComponents(anyString(), anyInt())).thenReturn(changes);
//Clear data
service.getBinderService().clearData("package", userId, false);
//Test migrate flow again
service.migrateDefaultNASShowNotificationIfNecessary();
//The notification should be still there
assertFalse(service.isNASMigrationDone(userId));
verify(service, times(2)).createNASUpgradeNotification(eq(userId));
verify(mAssistants, times(0)).resetDefaultFromConfig();
assertEquals(oldDefaultComponent, service.getApprovedAssistant(userId));
}
@Test
public void testNASSettingUpgrade_multiUser() throws RemoteException {
ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
ComponentName newDefaultComponent = ComponentName.unflattenFromString("package/Component2");
TestableNotificationManagerService service = spy(mService);
int userId1 = 11;
int userId2 = 12;
setUsers(new int[]{userId1, userId2});
setNASMigrationDone(false, userId1);
setNASMigrationDone(false, userId2);
when(mAssistants.getDefaultComponents())
.thenReturn(new ArraySet<>(Arrays.asList(oldDefaultComponent)));
when(mAssistants.getDefaultFromConfig())
.thenReturn(newDefaultComponent);
//User1: need onboarding
when(mAssistants.getAllowedComponents(userId1))
.thenReturn(Arrays.asList(oldDefaultComponent));
//User2: no onboarding
when(mAssistants.getAllowedComponents(userId2))
.thenReturn(Arrays.asList(newDefaultComponent));
when(mAssistants.hasUserSet(userId1)).thenReturn(true);
when(mAssistants.hasUserSet(userId2)).thenReturn(true);
service.migrateDefaultNASShowNotificationIfNecessary();
assertFalse(service.isNASMigrationDone(userId1));
assertTrue(service.isNASMigrationDone(userId2));
verify(service, times(1)).createNASUpgradeNotification(any(Integer.class));
// only user2's default get updated
verify(mAssistants, times(1)).resetDefaultFromConfig();
}
@Test
public void testNASSettingUpgrade_clearDataAfterMigrationIsDone() throws RemoteException {
ComponentName defaultComponent = ComponentName.unflattenFromString("package/Component");
TestableNotificationManagerService service = spy(mService);
int userId = 12;
setUsers(new int[]{userId});
when(mAssistants.getDefaultComponents())
.thenReturn(new ArraySet<>(Arrays.asList(defaultComponent)));
when(mAssistants.hasUserSet(userId)).thenReturn(true);
setNASMigrationDone(true, userId);
//Test User clear data
ArrayMap<Boolean, ArrayList<ComponentName>> changedListeners =
generateResetComponentValues();
when(mListeners.resetComponents(anyString(), anyInt())).thenReturn(changedListeners);
ArrayMap<Boolean, ArrayList<ComponentName>> changes = new ArrayMap<>();
changes.put(true, new ArrayList(Arrays.asList(defaultComponent)));
changes.put(false, new ArrayList());
when(mAssistants.resetComponents(anyString(), anyInt())).thenReturn(changes);
//Clear data
service.getBinderService().clearData("package", userId, false);
//Test migrate flow again
service.migrateDefaultNASShowNotificationIfNecessary();
//The notification should not appear again
verify(service, times(0)).createNASUpgradeNotification(eq(userId));
verify(mAssistants, times(0)).resetDefaultFromConfig();
}
@Test
public void testNASUpgradeNotificationDisableBroadcast() {
int userId = 11;
setUsers(new int[]{userId});
TestableNotificationManagerService service = spy(mService);
setNASMigrationDone(false, userId);
simulateNASUpgradeBroadcast(ACTION_DISABLE_NAS, userId);
assertTrue(service.isNASMigrationDone(userId));
// User disabled the NAS from notification, the default stored in xml should be null
// rather than the new default
verify(mAssistants, times(1)).clearDefaults();
verify(mAssistants, times(0)).resetDefaultFromConfig();
//No more notification after disabled
service.migrateDefaultNASShowNotificationIfNecessary();
verify(service, times(0)).createNASUpgradeNotification(eq(userId));
}
@Test
public void testNASUpgradeNotificationEnableBroadcast_multiUser() {
int userId1 = 11;
int userId2 = 12;
setUsers(new int[]{userId1, userId2});
TestableNotificationManagerService service = spy(mService);
setNASMigrationDone(false, userId1);
setNASMigrationDone(false, userId2);
simulateNASUpgradeBroadcast(ACTION_ENABLE_NAS, userId1);
assertTrue(service.isNASMigrationDone(userId1));
assertFalse(service.isNASMigrationDone(userId2));
verify(mAssistants, times(1)).resetDefaultFromConfig();
service.migrateDefaultNASShowNotificationIfNecessary();
verify(service, times(0)).createNASUpgradeNotification(eq(userId1));
}
@Test
public void testNASUpgradeNotificationLearnMoreBroadcast() {
int userId = 11;
setUsers(new int[]{userId});
TestableNotificationManagerService service = spy(mService);
setNASMigrationDone(false, userId);
doNothing().when(mContext).startActivity(any());
simulateNASUpgradeBroadcast(ACTION_LEARNMORE_NAS, userId);
verify(mContext, times(1)).startActivity(any(Intent.class));
assertFalse(service.isNASMigrationDone(userId));
verify(service, times(0)).createNASUpgradeNotification(eq(userId));
verify(mAssistants, times(0)).resetDefaultFromConfig();
}
private void setNASMigrationDone(boolean done, int userId) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.NAS_SETTINGS_UPDATED, done ? 1 : 0, userId);
}
private void setUsers(int[] userIds) {
List<UserInfo> users = new ArrayList<>();
for (int id: userIds) {
users.add(new UserInfo(id, String.valueOf(id), 0));
}
for (UserInfo user : users) {
when(mUm.getUserInfo(eq(user.id))).thenReturn(user);
}
when(mUm.getUsers()).thenReturn(users);
}
@Test
public void clearDefaultListenersPackageShouldEnableIt() throws RemoteException {
ArrayMap<Boolean, ArrayList<ComponentName>> changedAssistants =