Move ServiceListing to SettingsLib
Also tidy things up a bit Bug: 70902607 Test: RunSettingsRoboTests Change-Id: Id641beb601513bb4e34a098bf2729eb98954175d
This commit is contained in:
@@ -27,20 +27,16 @@ import com.android.settings.utils.ManagedServiceSettings;
|
||||
|
||||
public class VrListenerSettings extends ManagedServiceSettings {
|
||||
private static final String TAG = VrListenerSettings.class.getSimpleName();
|
||||
private static final Config CONFIG = getVrListenerConfig();
|
||||
|
||||
private static final Config getVrListenerConfig() {
|
||||
final Config c = new Config();
|
||||
c.tag = TAG;
|
||||
c.setting = Settings.Secure.ENABLED_VR_LISTENERS;
|
||||
c.intentAction = VrListenerService.SERVICE_INTERFACE;
|
||||
c.permission = android.Manifest.permission.BIND_VR_LISTENER_SERVICE;
|
||||
c.noun = "vr listener";
|
||||
c.warningDialogTitle = R.string.vr_listener_security_warning_title;
|
||||
c.warningDialogSummary = R.string.vr_listener_security_warning_summary;
|
||||
c.emptyText = R.string.no_vr_listeners;
|
||||
return c;
|
||||
}
|
||||
private static final Config CONFIG = new Config.Builder()
|
||||
.setTag(TAG)
|
||||
.setSetting(Settings.Secure.ENABLED_VR_LISTENERS)
|
||||
.setIntentAction(VrListenerService.SERVICE_INTERFACE)
|
||||
.setPermission(android.Manifest.permission.BIND_VR_LISTENER_SERVICE)
|
||||
.setNoun("vr listener")
|
||||
.setWarningDialogTitle(R.string.vr_listener_security_warning_title)
|
||||
.setWarningDialogSummary(R.string.vr_listener_security_warning_summary)
|
||||
.setEmptyText(R.string.no_vr_listeners)
|
||||
.build();
|
||||
|
||||
@Override
|
||||
protected Config getConfig() {
|
||||
|
@@ -19,8 +19,9 @@ package com.android.settings.notification;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
@@ -33,28 +34,35 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.utils.ManagedServiceSettings;
|
||||
|
||||
/**
|
||||
* Settings screen for managing notification listener permissions
|
||||
*/
|
||||
public class NotificationAccessSettings extends ManagedServiceSettings {
|
||||
private static final String TAG = NotificationAccessSettings.class.getSimpleName();
|
||||
private static final Config CONFIG = getNotificationListenerConfig();
|
||||
private static final Config CONFIG = new Config.Builder()
|
||||
.setTag(TAG)
|
||||
.setSetting(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS)
|
||||
.setIntentAction(NotificationListenerService.SERVICE_INTERFACE)
|
||||
.setPermission(android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE)
|
||||
.setNoun("notification listener")
|
||||
.setWarningDialogTitle(R.string.notification_listener_security_warning_title)
|
||||
.setWarningDialogSummary(R.string.notification_listener_security_warning_summary)
|
||||
.setEmptyText(R.string.no_notification_listeners)
|
||||
.build();
|
||||
|
||||
private static Config getNotificationListenerConfig() {
|
||||
final Config c = new Config();
|
||||
c.tag = TAG;
|
||||
c.setting = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
|
||||
c.intentAction = NotificationListenerService.SERVICE_INTERFACE;
|
||||
c.permission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
|
||||
c.noun = "notification listener";
|
||||
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;
|
||||
}
|
||||
private NotificationManager mNm;
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return MetricsEvent.NOTIFICATION_ACCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
mNm = context.getSystemService(NotificationManager.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Config getConfig() {
|
||||
return CONFIG;
|
||||
@@ -109,13 +117,10 @@ public class NotificationAccessSettings extends ManagedServiceSettings {
|
||||
|
||||
private static void disable(final NotificationAccessSettings parent, final ComponentName cn) {
|
||||
parent.mNm.setNotificationListenerAccessGranted(cn, false);
|
||||
AsyncTask.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!parent.mNm.isNotificationPolicyAccessGrantedForPackage(
|
||||
cn.getPackageName())) {
|
||||
parent.mNm.removeAutomaticZenRules(cn.getPackageName());
|
||||
}
|
||||
AsyncTask.execute(() -> {
|
||||
if (!parent.mNm.isNotificationPolicyAccessGrantedForPackage(
|
||||
cn.getPackageName())) {
|
||||
parent.mNm.removeAutomaticZenRules(cn.getPackageName());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -153,16 +158,10 @@ public class NotificationAccessSettings extends ManagedServiceSettings {
|
||||
.setMessage(summary)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.notification_listener_disable_warning_confirm,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
disable(parent, cn);
|
||||
}
|
||||
})
|
||||
(dialog, id) -> disable(parent, cn))
|
||||
.setNegativeButton(R.string.notification_listener_disable_warning_cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
// pass
|
||||
}
|
||||
(dialog, id) -> {
|
||||
// pass
|
||||
})
|
||||
.create();
|
||||
}
|
||||
|
@@ -64,12 +64,12 @@ public class ZenModeAutomationSettings extends ZenModeSettingsBase {
|
||||
}
|
||||
|
||||
protected static ManagedServiceSettings.Config getConditionProviderConfig() {
|
||||
final ManagedServiceSettings.Config c = new ManagedServiceSettings.Config();
|
||||
c.tag = TAG;
|
||||
c.intentAction = ConditionProviderService.SERVICE_INTERFACE;
|
||||
c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
|
||||
c.noun = "condition provider";
|
||||
return c;
|
||||
return new ManagedServiceSettings.Config.Builder()
|
||||
.setTag(TAG)
|
||||
.setIntentAction(ConditionProviderService.SERVICE_INTERFACE)
|
||||
.setPermission(android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE)
|
||||
.setNoun("condition provider")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -21,11 +21,9 @@ import android.app.ActivityManager;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.Fragment;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.pm.PackageItemInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
@@ -33,8 +31,6 @@ import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.support.v14.preference.SwitchPreference;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.support.v7.preference.PreferenceScreen;
|
||||
import android.util.IconDrawableFactory;
|
||||
import android.util.Log;
|
||||
@@ -46,8 +42,8 @@ import com.android.settings.Utils;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settings.notification.EmptyTextSettings;
|
||||
import com.android.settings.widget.AppSwitchPreference;
|
||||
import com.android.settingslib.applications.ServiceListing;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class ManagedServiceSettings extends EmptyTextSettings {
|
||||
@@ -57,8 +53,7 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings {
|
||||
protected Context mContext;
|
||||
private PackageManager mPm;
|
||||
private DevicePolicyManager mDpm;
|
||||
protected ServiceListing mServiceListing;
|
||||
protected NotificationManager mNm;
|
||||
private ServiceListing mServiceListing;
|
||||
private IconDrawableFactory mIconDrawableFactory;
|
||||
|
||||
abstract protected Config getConfig();
|
||||
@@ -74,15 +69,15 @@ 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);
|
||||
mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
|
||||
mServiceListing = new ServiceListing(mContext, mConfig);
|
||||
mServiceListing.addCallback(new ServiceListing.Callback() {
|
||||
@Override
|
||||
public void onServicesReloaded(List<ServiceInfo> services) {
|
||||
updateList(services);
|
||||
}
|
||||
});
|
||||
mServiceListing = new ServiceListing.Builder(mContext)
|
||||
.setPermission(mConfig.permission)
|
||||
.setIntentAction(mConfig.intentAction)
|
||||
.setNoun(mConfig.noun)
|
||||
.setSetting(mConfig.setting)
|
||||
.setTag(mConfig.tag)
|
||||
.build();
|
||||
mServiceListing.addCallback(this::updateList);
|
||||
setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext));
|
||||
}
|
||||
|
||||
@@ -115,7 +110,7 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings {
|
||||
|
||||
final PreferenceScreen screen = getPreferenceScreen();
|
||||
screen.removeAll();
|
||||
Collections.sort(services, new PackageItemInfo.DisplayNameComparator(mPm));
|
||||
services.sort(new PackageItemInfo.DisplayNameComparator(mPm));
|
||||
for (ServiceInfo service : services) {
|
||||
final ComponentName cn = new ComponentName(service.packageName, service.name);
|
||||
CharSequence title = null;
|
||||
@@ -144,12 +139,9 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings {
|
||||
service.packageName, managedProfileId)) {
|
||||
pref.setSummary(R.string.work_profile_notification_access_blocked_summary);
|
||||
}
|
||||
pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
final boolean enable = (boolean) newValue;
|
||||
return setEnabled(cn, summary, enable);
|
||||
}
|
||||
pref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
final boolean enable = (boolean) newValue;
|
||||
return setEnabled(cn, summary, enable);
|
||||
});
|
||||
screen.addPreference(pref);
|
||||
}
|
||||
@@ -188,8 +180,8 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings {
|
||||
}
|
||||
|
||||
public static class ScaryWarningDialogFragment extends InstrumentedDialogFragment {
|
||||
static final String KEY_COMPONENT = "c";
|
||||
static final String KEY_LABEL = "l";
|
||||
private static final String KEY_COMPONENT = "c";
|
||||
private static final String KEY_LABEL = "l";
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
@@ -222,29 +214,92 @@ public abstract class ManagedServiceSettings extends EmptyTextSettings {
|
||||
.setTitle(title)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.allow,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
parent.enable(cn);
|
||||
}
|
||||
})
|
||||
(dialog, id) -> parent.enable(cn))
|
||||
.setNegativeButton(R.string.deny,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
// pass
|
||||
}
|
||||
(dialog, id) -> {
|
||||
// pass
|
||||
})
|
||||
.create();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
public String tag;
|
||||
public String setting;
|
||||
public String intentAction;
|
||||
public String permission;
|
||||
public String noun;
|
||||
public int warningDialogTitle;
|
||||
public int warningDialogSummary;
|
||||
public int emptyText;
|
||||
public final String tag;
|
||||
public final String setting;
|
||||
public final String intentAction;
|
||||
public final String permission;
|
||||
public final String noun;
|
||||
public final int warningDialogTitle;
|
||||
public final int warningDialogSummary;
|
||||
public final int emptyText;
|
||||
|
||||
private Config(String tag, String setting, String intentAction, String permission,
|
||||
String noun, int warningDialogTitle, int warningDialogSummary, int emptyText) {
|
||||
this.tag = tag;
|
||||
this.setting = setting;
|
||||
this.intentAction = intentAction;
|
||||
this.permission = permission;
|
||||
this.noun = noun;
|
||||
this.warningDialogTitle = warningDialogTitle;
|
||||
this.warningDialogSummary = warningDialogSummary;
|
||||
this.emptyText = emptyText;
|
||||
}
|
||||
|
||||
public static class Builder{
|
||||
private String mTag;
|
||||
private String mSetting;
|
||||
private String mIntentAction;
|
||||
private String mPermission;
|
||||
private String mNoun;
|
||||
private int mWarningDialogTitle;
|
||||
private int mWarningDialogSummary;
|
||||
private int mEmptyText;
|
||||
|
||||
public Builder setTag(String tag) {
|
||||
mTag = tag;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setSetting(String setting) {
|
||||
mSetting = setting;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setIntentAction(String intentAction) {
|
||||
mIntentAction = intentAction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setPermission(String permission) {
|
||||
mPermission = permission;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setNoun(String noun) {
|
||||
mNoun = noun;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setWarningDialogTitle(int warningDialogTitle) {
|
||||
mWarningDialogTitle = warningDialogTitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setWarningDialogSummary(int warningDialogSummary) {
|
||||
mWarningDialogSummary = warningDialogSummary;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setEmptyText(int emptyText) {
|
||||
mEmptyText = emptyText;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Config build() {
|
||||
return new Config(mTag, mSetting, mIntentAction, mPermission, mNoun,
|
||||
mWarningDialogTitle, mWarningDialogSummary, mEmptyText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 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.utils;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.database.ContentObserver;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.settings.utils.ManagedServiceSettings.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
public class ServiceListing {
|
||||
private final ContentResolver mContentResolver;
|
||||
private final Context mContext;
|
||||
private final Config mConfig;
|
||||
private final HashSet<ComponentName> mEnabledServices = new HashSet<ComponentName>();
|
||||
private final List<ServiceInfo> mServices = new ArrayList<ServiceInfo>();
|
||||
private final List<Callback> mCallbacks = new ArrayList<Callback>();
|
||||
|
||||
private boolean mListening;
|
||||
|
||||
public ServiceListing(Context context, Config config) {
|
||||
mContext = context;
|
||||
mConfig = config;
|
||||
mContentResolver = context.getContentResolver();
|
||||
}
|
||||
|
||||
public void addCallback(Callback callback) {
|
||||
mCallbacks.add(callback);
|
||||
}
|
||||
|
||||
public void removeCallback(Callback callback) {
|
||||
mCallbacks.remove(callback);
|
||||
}
|
||||
|
||||
public void setListening(boolean listening) {
|
||||
if (mListening == listening) return;
|
||||
mListening = listening;
|
||||
if (mListening) {
|
||||
// listen for package changes
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
|
||||
filter.addDataScheme("package");
|
||||
mContext.registerReceiver(mPackageReceiver, filter);
|
||||
mContentResolver.registerContentObserver(Settings.Secure.getUriFor(mConfig.setting),
|
||||
false, mSettingsObserver);
|
||||
} else {
|
||||
mContext.unregisterReceiver(mPackageReceiver);
|
||||
mContentResolver.unregisterContentObserver(mSettingsObserver);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getEnabledServicesCount(Config config, Context context) {
|
||||
final String flat = Settings.Secure.getString(context.getContentResolver(), config.setting);
|
||||
if (flat == null || "".equals(flat)) return 0;
|
||||
final String[] components = flat.split(":");
|
||||
return components.length;
|
||||
}
|
||||
|
||||
public static int getServicesCount(Config c, PackageManager pm) {
|
||||
return getServices(c, null, pm);
|
||||
}
|
||||
|
||||
protected static int getServices(Config c, List<ServiceInfo> list, PackageManager pm) {
|
||||
int services = 0;
|
||||
if (list != null) {
|
||||
list.clear();
|
||||
}
|
||||
final int user = ActivityManager.getCurrentUser();
|
||||
|
||||
List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
|
||||
new Intent(c.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 (!c.permission.equals(info.permission)) {
|
||||
Slog.w(c.tag, "Skipping " + c.noun + " service "
|
||||
+ info.packageName + "/" + info.name
|
||||
+ ": it does not require the permission "
|
||||
+ c.permission);
|
||||
continue;
|
||||
}
|
||||
if (list != null) {
|
||||
list.add(info);
|
||||
}
|
||||
services++;
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
private void saveEnabledServices() {
|
||||
StringBuilder sb = null;
|
||||
for (ComponentName cn : mEnabledServices) {
|
||||
if (sb == null) {
|
||||
sb = new StringBuilder();
|
||||
} else {
|
||||
sb.append(':');
|
||||
}
|
||||
sb.append(cn.flattenToString());
|
||||
}
|
||||
Settings.Secure.putString(mContentResolver, mConfig.setting,
|
||||
sb != null ? sb.toString() : "");
|
||||
}
|
||||
|
||||
private void loadEnabledServices() {
|
||||
mEnabledServices.clear();
|
||||
final String flat = Settings.Secure.getString(mContentResolver, mConfig.setting);
|
||||
if (flat != null && !"".equals(flat)) {
|
||||
final String[] names = flat.split(":");
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
final ComponentName cn = ComponentName.unflattenFromString(names[i]);
|
||||
if (cn != null) {
|
||||
mEnabledServices.add(cn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<ServiceInfo> reload() {
|
||||
loadEnabledServices();
|
||||
getServices(mConfig, mServices, mContext.getPackageManager());
|
||||
for (Callback callback : mCallbacks) {
|
||||
callback.onServicesReloaded(mServices);
|
||||
}
|
||||
return mServices;
|
||||
}
|
||||
|
||||
public boolean isEnabled(ComponentName cn) {
|
||||
return mEnabledServices.contains(cn);
|
||||
}
|
||||
|
||||
public void setEnabled(ComponentName cn, boolean enabled) {
|
||||
if (enabled) {
|
||||
mEnabledServices.add(cn);
|
||||
} else {
|
||||
mEnabledServices.remove(cn);
|
||||
}
|
||||
saveEnabledServices();
|
||||
}
|
||||
|
||||
private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
reload();
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
reload();
|
||||
}
|
||||
};
|
||||
|
||||
public interface Callback {
|
||||
void onServicesReloaded(List<ServiceInfo> services);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user