diff --git a/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java b/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java deleted file mode 100644 index 8ffcb68f4d1..00000000000 --- a/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPicker.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2017 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.settings.applications.defaultapps; - -import android.app.ActivityManager; -import android.content.ComponentName; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.provider.Settings; -import android.service.notification.NotificationAssistantService; -import android.util.Slog; - -import com.android.settings.R; -import com.android.settings.utils.ManagedServiceSettings; - -import java.util.ArrayList; -import java.util.List; - -public class DefaultNotificationAssistantPicker extends DefaultAppPickerFragment { - private static final String TAG = "DefaultNotiAssist"; - - private final ManagedServiceSettings.Config mConfig = getConfig(); - - @Override - public int getMetricsCategory() { - return 0; - } - - @Override - protected String getDefaultKey() { - return Settings.Secure.getString(getContext().getContentResolver(), mConfig.setting); - } - - @Override - protected boolean setDefaultKey(String value) { - Settings.Secure.putString(getContext().getContentResolver(), mConfig.setting, value); - return true; - } - - @Override - protected List getCandidates() { - List candidates = new ArrayList<>(); - final int user = ActivityManager.getCurrentUser(); - - List installedServices = mPm.queryIntentServicesAsUser( - new Intent(mConfig.intentAction), - PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, - user); - - for (int i = 0, count = installedServices.size(); i < count; i++) { - ResolveInfo resolveInfo = installedServices.get(i); - ServiceInfo info = resolveInfo.serviceInfo; - - if (!mConfig.permission.equals(info.permission)) { - Slog.w(mConfig.tag, "Skipping " + mConfig.noun + " service " - + info.packageName + "/" + info.name - + ": it does not require the permission " - + mConfig.permission); - continue; - } - - candidates.add(new DefaultAppInfo(mPm, - mUserId, new ComponentName(info.packageName, info.name))); - } - return candidates; - } - - @Override - protected boolean shouldShowItemNone() { - return true; - } - - private ManagedServiceSettings.Config getConfig() { - final ManagedServiceSettings.Config c = new ManagedServiceSettings.Config(); - c.tag = TAG; - c.setting = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT; - c.intentAction = NotificationAssistantService.SERVICE_INTERFACE; - c.permission = android.Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE; - c.noun = "notification assistant"; - c.warningDialogTitle = R.string.notification_listener_security_warning_title; - c.warningDialogSummary = R.string.notification_listener_security_warning_summary; - c.emptyText = R.string.no_notification_listeners; - return c; - } -} diff --git a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java index a78fed7f35b..6a13282285d 100644 --- a/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java +++ b/src/com/android/settings/notification/NotificationAccessConfirmationActivity.java @@ -27,7 +27,9 @@ import static com.android.internal.notification.NotificationAccessConfirmationAc import android.Manifest; import android.annotation.Nullable; import android.app.Activity; +import android.app.NotificationManager; import android.content.ComponentName; +import android.content.Context; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.content.pm.ServiceInfo; @@ -53,12 +55,14 @@ public class NotificationAccessConfirmationActivity extends Activity private int mUserId; private ComponentName mComponentName; private TouchOverlayManager mTouchOverlayManager; + private NotificationManager mNm; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mTouchOverlayManager = new TouchOverlayManager(this); + mNm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mComponentName = getIntent().getParcelableExtra(EXTRA_COMPONENT_NAME); mUserId = getIntent().getIntExtra(EXTRA_USER_ID, UserHandle.USER_NULL); @@ -94,12 +98,7 @@ public class NotificationAccessConfirmationActivity extends Activity return; } - final SettingsStringUtil.SettingStringHelper setting = - new SettingsStringUtil.SettingStringHelper( - getContentResolver(), - Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, - mUserId); - setting.write(SettingsStringUtil.ComponentNameSet.add(setting.read(), mComponentName)); + mNm.setNotificationListenerAccessGranted(mComponentName, true); finish(); } diff --git a/src/com/android/settings/notification/NotificationAccessSettings.java b/src/com/android/settings/notification/NotificationAccessSettings.java index 2cd728cb1d7..37601b36b84 100644 --- a/src/com/android/settings/notification/NotificationAccessSettings.java +++ b/src/com/android/settings/notification/NotificationAccessSettings.java @@ -41,7 +41,6 @@ public class NotificationAccessSettings extends ManagedServiceSettings { private static final String TAG = NotificationAccessSettings.class.getSimpleName(); private static final Config CONFIG = getNotificationListenerConfig(); - @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -70,10 +69,11 @@ public class NotificationAccessSettings extends ManagedServiceSettings { return CONFIG; } + @Override protected boolean setEnabled(ComponentName service, String title, boolean enable) { logSpecialPermissionChange(enable, service.getPackageName()); if (!enable) { - if (!mServiceListing.isEnabled(service)) { + if (!isServiceEnabled(service)) { return true; // already disabled } // show a friendly dialog @@ -82,10 +82,27 @@ public class NotificationAccessSettings extends ManagedServiceSettings { .show(getFragmentManager(), "friendlydialog"); return false; } else { - return super.setEnabled(service, title, enable); + if (isServiceEnabled(service)) { + return true; // already enabled + } + // show a scary dialog + new ScaryWarningDialogFragment() + .setServiceInfo(service, title, this) + .show(getFragmentManager(), "dialog"); + return false; } } + @Override + protected boolean isServiceEnabled(ComponentName cn) { + return mNm.isNotificationListenerAccessGranted(cn); + } + + @Override + protected void enable(ComponentName service) { + mNm.setNotificationListenerAccessGranted(service, true); + } + @VisibleForTesting void logSpecialPermissionChange(boolean enable, String packageName) { int logCategory = enable ? MetricsEvent.APP_SPECIAL_PERMISSION_NOTIVIEW_ALLOW @@ -94,17 +111,14 @@ public class NotificationAccessSettings extends ManagedServiceSettings { logCategory, packageName); } - private static void disable(final Context context, final NotificationAccessSettings parent, - final ComponentName cn) { - parent.mServiceListing.setEnabled(cn, false); + private static void disable(final NotificationAccessSettings parent, final ComponentName cn) { + parent.mNm.setNotificationListenerAccessGranted(cn, false); AsyncTask.execute(new Runnable() { @Override public void run() { - final NotificationManager mgr = context.getSystemService(NotificationManager.class); - - if (!mgr.isNotificationPolicyAccessGrantedForPackage( + if (!parent.mNm.isNotificationPolicyAccessGrantedForPackage( cn.getPackageName())) { - mgr.removeAutomaticZenRules(cn.getPackageName()); + parent.mNm.removeAutomaticZenRules(cn.getPackageName()); } } }); @@ -145,7 +159,7 @@ public class NotificationAccessSettings extends ManagedServiceSettings { .setPositiveButton(R.string.notification_listener_disable_warning_confirm, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - disable(getContext(), parent, cn); + disable(parent, cn); } }) .setNegativeButton(R.string.notification_listener_disable_warning_cancel, diff --git a/src/com/android/settings/notification/ZenAccessSettings.java b/src/com/android/settings/notification/ZenAccessSettings.java index a41a7339d0c..0d3798079ad 100644 --- a/src/com/android/settings/notification/ZenAccessSettings.java +++ b/src/com/android/settings/notification/ZenAccessSettings.java @@ -17,7 +17,9 @@ package com.android.settings.notification; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.AlertDialog; +import android.app.AppGlobals; import android.app.Dialog; import android.app.DialogFragment; import android.app.NotificationManager; @@ -25,14 +27,17 @@ import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; +import android.content.pm.ParceledListSlice; import android.database.ContentObserver; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.os.RemoteException; import android.provider.Settings; import android.provider.Settings.Secure; import android.support.v14.preference.SwitchPreference; @@ -41,6 +46,7 @@ import android.support.v7.preference.Preference.OnPreferenceChangeListener; import android.support.v7.preference.PreferenceScreen; import android.text.TextUtils; import android.util.ArraySet; +import android.util.Log; import android.view.View; import android.widget.Toast; @@ -55,10 +61,9 @@ import java.util.Collections; import java.util.List; public class ZenAccessSettings extends EmptyTextSettings { + private final String TAG = "ZenAccessSettings"; private final SettingObserver mObserver = new SettingObserver(); - private static final String ENABLED_SERVICES_SEPARATOR = ":"; - private Context mContext; private PackageManager mPkgMan; private NotificationManager mNoMan; @@ -106,7 +111,7 @@ public class ZenAccessSettings extends EmptyTextSettings { final PreferenceScreen screen = getPreferenceScreen(); screen.removeAll(); final ArrayList apps = new ArrayList<>(); - final ArraySet requesting = mNoMan.getPackagesRequestingNotificationPolicyAccess(); + final ArraySet requesting = getPackagesRequestingNotificationPolicyAccess(); if (!requesting.isEmpty()) { final List installed = mPkgMan.getInstalledApplications(0); if (installed != null) { @@ -117,7 +122,8 @@ public class ZenAccessSettings extends EmptyTextSettings { } } } - ArraySet autoApproved = getEnabledNotificationListeners(); + ArraySet autoApproved = new ArraySet<>(); + autoApproved.addAll(mNoMan.getEnabledNotificationListenerPackages()); requesting.addAll(autoApproved); Collections.sort(apps, new PackageItemInfo.DisplayNameComparator(mPkgMan)); for (ApplicationInfo app : apps) { @@ -152,20 +158,25 @@ public class ZenAccessSettings extends EmptyTextSettings { } } - private ArraySet getEnabledNotificationListeners() { - ArraySet packages = new ArraySet<>(); - String settingValue = Settings.Secure.getString(getContext().getContentResolver(), - Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); - if (!TextUtils.isEmpty(settingValue)) { - String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR); - for (int i = 0; i < restored.length; i++) { - ComponentName value = ComponentName.unflattenFromString(restored[i]); - if (null != value) { - packages.add(value.getPackageName()); + private ArraySet getPackagesRequestingNotificationPolicyAccess() { + ArraySet requestingPackages = new ArraySet<>(); + try { + final String[] PERM = { + android.Manifest.permission.ACCESS_NOTIFICATION_POLICY + }; + final ParceledListSlice list = AppGlobals.getPackageManager() + .getPackagesHoldingPermissions(PERM, 0 /*flags*/, + ActivityManager.getCurrentUser()); + final List pkgs = list.getList(); + if (pkgs != null) { + for (PackageInfo info : pkgs) { + requestingPackages.add(info.packageName); } } + } catch(RemoteException e) { + Log.e(TAG, "Cannot reach packagemanager", e); } - return packages; + return requestingPackages; } private boolean hasAccess(String pkg) { diff --git a/src/com/android/settings/notification/ZenModeSettings.java b/src/com/android/settings/notification/ZenModeSettings.java index 854857ada45..0d9c78f37c1 100644 --- a/src/com/android/settings/notification/ZenModeSettings.java +++ b/src/com/android/settings/notification/ZenModeSettings.java @@ -179,8 +179,6 @@ public class ZenModeSettings extends ZenModeSettingsBase { private static ManagedServiceSettings.Config getConditionProviderConfig() { final ManagedServiceSettings.Config c = new ManagedServiceSettings.Config(); c.tag = TAG; - c.setting = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES; - c.secondarySetting = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; c.intentAction = ConditionProviderService.SERVICE_INTERFACE; c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE; c.noun = "condition provider"; diff --git a/src/com/android/settings/utils/ManagedServiceSettings.java b/src/com/android/settings/utils/ManagedServiceSettings.java index 6e3841dfdfb..d76520fbfed 100644 --- a/src/com/android/settings/utils/ManagedServiceSettings.java +++ b/src/com/android/settings/utils/ManagedServiceSettings.java @@ -20,6 +20,8 @@ import android.annotation.Nullable; import android.app.AlertDialog; import android.app.Dialog; import android.app.Fragment; +import android.app.Notification; +import android.app.NotificationManager; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.Context; @@ -54,6 +56,7 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings { private PackageManager mPm; private DevicePolicyManager mDpm; protected ServiceListing mServiceListing; + protected NotificationManager mNm; abstract protected Config getConfig(); @@ -68,6 +71,7 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings { mContext = getActivity(); mPm = mContext.getPackageManager(); mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); + mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mServiceListing = new ServiceListing(mContext, mConfig); mServiceListing.addCallback(new ServiceListing.Callback() { @Override @@ -124,7 +128,7 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings { } else { pref.setTitle(summary); } - pref.setChecked(mServiceListing.isEnabled(cn)); + pref.setChecked(isServiceEnabled(cn)); if (managedProfileId != UserHandle.USER_NULL && !mDpm.isNotificationListenerServicePermitted( service.packageName, managedProfileId)) { @@ -148,6 +152,10 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings { return UserHandle.myUserId(); } + protected boolean isServiceEnabled(ComponentName cn) { + return mServiceListing.isEnabled(cn); + } + protected boolean setEnabled(ComponentName service, String title, boolean enable) { if (!enable) { // the simple version: disabling @@ -165,6 +173,10 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings { } } + protected void enable(ComponentName service) { + mServiceListing.setEnabled(service, true); + } + public static class ScaryWarningDialogFragment extends InstrumentedDialogFragment { static final String KEY_COMPONENT = "c"; static final String KEY_LABEL = "l"; @@ -202,7 +214,7 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings { .setPositiveButton(R.string.allow, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { - parent.mServiceListing.setEnabled(cn, true); + parent.enable(cn); } }) .setNegativeButton(R.string.deny, diff --git a/src/com/android/settings/utils/ZenServiceListing.java b/src/com/android/settings/utils/ZenServiceListing.java index 167b066302d..40a4f34dfae 100644 --- a/src/com/android/settings/utils/ZenServiceListing.java +++ b/src/com/android/settings/utils/ZenServiceListing.java @@ -16,6 +16,7 @@ package com.android.settings.utils; import android.app.ActivityManager; +import android.app.NotificationManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -35,16 +36,16 @@ import java.util.Set; public class ZenServiceListing { - private final ContentResolver mContentResolver; private final Context mContext; private final ManagedServiceSettings.Config mConfig; private final Set mApprovedServices = new ArraySet(); private final List mZenCallbacks = new ArrayList<>(); + private final NotificationManager mNm; public ZenServiceListing(Context context, ManagedServiceSettings.Config config) { mContext = context; mConfig = config; - mContentResolver = context.getContentResolver(); + mNm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); } public ServiceInfo findService(final ComponentName cn) { @@ -67,23 +68,18 @@ public class ZenServiceListing { public void reloadApprovedServices() { mApprovedServices.clear(); - String[] settings = {mConfig.setting, mConfig.secondarySetting}; - for (String setting : settings) { - if (!TextUtils.isEmpty(setting)) { - final String flat = Settings.Secure.getString(mContentResolver, setting); - if (!TextUtils.isEmpty(flat)) { - final List names = Arrays.asList(flat.split(":")); - List services = new ArrayList<>(); - getServices(mConfig, services, mContext.getPackageManager()); - for (ServiceInfo service : services) { - if (matchesApprovedPackage(names, service.getComponentName())) { - mApprovedServices.add(service); - } - } - } + List enabledNotificationListenerPkgs = mNm.getEnabledNotificationListenerPackages(); + List services = new ArrayList<>(); + getServices(mConfig, services, mContext.getPackageManager()); + for (ServiceInfo service : services) { + final String servicePackage = service.getComponentName().getPackageName(); + if (mNm.isNotificationPolicyAccessGrantedForPackage(servicePackage) + || enabledNotificationListenerPkgs.contains(servicePackage)) { + mApprovedServices.add(service); } } + if (!mApprovedServices.isEmpty()) { for (Callback callback : mZenCallbacks) { callback.onServicesReloaded(mApprovedServices); @@ -91,25 +87,6 @@ public class ZenServiceListing { } } - // Setting could contain: the component name of the condition provider, the package name of - // the condition provider, the component name of the notification listener. - private boolean matchesApprovedPackage(List approved, ComponentName serviceOwner) { - String flatCn = serviceOwner.flattenToString(); - if (approved.contains(flatCn) || approved.contains(serviceOwner.getPackageName())) { - return true; - } - for (String entry : approved) { - if (!TextUtils.isEmpty(entry)) { - ComponentName approvedComponent = ComponentName.unflattenFromString(entry); - if (approvedComponent != null && approvedComponent.getPackageName().equals( - serviceOwner.getPackageName())) { - return true; - } - } - } - return false; - } - private static int getServices(ManagedServiceSettings.Config c, List list, PackageManager pm) { int services = 0; diff --git a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPickerTest.java b/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPickerTest.java deleted file mode 100644 index a60b43c9fe6..00000000000 --- a/tests/robotests/src/com/android/settings/applications/defaultapps/DefaultNotificationAssistantPickerTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2017 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.settings.applications.defaultapps; - - -import android.app.Activity; -import android.content.Context; -import android.os.UserManager; -import android.provider.Settings; - -import com.android.settings.testutils.SettingsRobolectricTestRunner; -import com.android.settings.TestConfig; -import com.android.settings.applications.PackageManagerWrapper; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; -import org.robolectric.util.ReflectionHelpers; - -import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -@RunWith(SettingsRobolectricTestRunner.class) -@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) -public class DefaultNotificationAssistantPickerTest { - - private static final String TEST_APP_KEY = "com.android.settings/PickerTest"; - - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - private Activity mActivity; - @Mock - private UserManager mUserManager; - @Mock - private PackageManagerWrapper mPackageManager; - - private DefaultNotificationAssistantPicker mPicker; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(mActivity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); - - mPicker = spy(new DefaultNotificationAssistantPicker()); - mPicker.onAttach((Context) mActivity); - - ReflectionHelpers.setField(mPicker, "mPm", mPackageManager); - doReturn(RuntimeEnvironment.application).when(mPicker).getContext(); - } - - @Test - public void setDefaultAppKey_shouldUpdateDefault() { - mPicker.setDefaultKey(TEST_APP_KEY); - - assertThat(mPicker.getDefaultKey()).isEqualTo(TEST_APP_KEY); - } - - @Test - public void getDefaultAppKey_shouldReturnDefault() { - Settings.Secure.putString(RuntimeEnvironment.application.getContentResolver(), - Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT, - TEST_APP_KEY); - - assertThat(mPicker.getDefaultKey()).isEqualTo(TEST_APP_KEY); - } -}