1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.applications; 18 19 import android.app.ActivityManager; 20 import android.content.BroadcastReceiver; 21 import android.content.ComponentName; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.pm.PackageManager; 27 import android.content.pm.ResolveInfo; 28 import android.content.pm.ServiceInfo; 29 import android.database.ContentObserver; 30 import android.net.Uri; 31 import android.os.Handler; 32 import android.provider.Settings; 33 import android.util.Slog; 34 35 import java.util.ArrayList; 36 import java.util.HashSet; 37 import java.util.List; 38 import java.util.function.Predicate; 39 40 /** 41 * Class for managing services matching a given intent and requesting a given permission. 42 */ 43 public class ServiceListing { 44 private final ContentResolver mContentResolver; 45 private final Context mContext; 46 private final String mTag; 47 private final String mSetting; 48 private final String mIntentAction; 49 private final String mPermission; 50 private final String mNoun; 51 private final boolean mAddDeviceLockedFlags; 52 private final HashSet<ComponentName> mEnabledServices = new HashSet<>(); 53 private final List<ServiceInfo> mServices = new ArrayList<>(); 54 private final List<Callback> mCallbacks = new ArrayList<>(); 55 private final Predicate mValidator; 56 57 private boolean mListening; 58 ServiceListing(Context context, String tag, String setting, String intentAction, String permission, String noun, boolean addDeviceLockedFlags, Predicate validator)59 private ServiceListing(Context context, String tag, 60 String setting, String intentAction, String permission, String noun, 61 boolean addDeviceLockedFlags, Predicate validator) { 62 mContentResolver = context.getContentResolver(); 63 mContext = context; 64 mTag = tag; 65 mSetting = setting; 66 mIntentAction = intentAction; 67 mPermission = permission; 68 mNoun = noun; 69 mAddDeviceLockedFlags = addDeviceLockedFlags; 70 mValidator = validator; 71 } 72 addCallback(Callback callback)73 public void addCallback(Callback callback) { 74 mCallbacks.add(callback); 75 } 76 removeCallback(Callback callback)77 public void removeCallback(Callback callback) { 78 mCallbacks.remove(callback); 79 } 80 setListening(boolean listening)81 public void setListening(boolean listening) { 82 if (mListening == listening) return; 83 mListening = listening; 84 if (mListening) { 85 // listen for package changes 86 IntentFilter filter = new IntentFilter(); 87 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 88 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 89 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 90 filter.addAction(Intent.ACTION_PACKAGE_REPLACED); 91 filter.addDataScheme("package"); 92 mContext.registerReceiver(mPackageReceiver, filter); 93 mContentResolver.registerContentObserver(Settings.Secure.getUriFor(mSetting), 94 false, mSettingsObserver); 95 } else { 96 mContext.unregisterReceiver(mPackageReceiver); 97 mContentResolver.unregisterContentObserver(mSettingsObserver); 98 } 99 } 100 saveEnabledServices()101 private void saveEnabledServices() { 102 StringBuilder sb = null; 103 for (ComponentName cn : mEnabledServices) { 104 if (sb == null) { 105 sb = new StringBuilder(); 106 } else { 107 sb.append(':'); 108 } 109 sb.append(cn.flattenToString()); 110 } 111 Settings.Secure.putString(mContentResolver, mSetting, 112 sb != null ? sb.toString() : ""); 113 } 114 loadEnabledServices()115 private void loadEnabledServices() { 116 mEnabledServices.clear(); 117 final String flat = Settings.Secure.getString(mContentResolver, mSetting); 118 if (flat != null && !"".equals(flat)) { 119 final String[] names = flat.split(":"); 120 for (String name : names) { 121 final ComponentName cn = ComponentName.unflattenFromString(name); 122 if (cn != null) { 123 mEnabledServices.add(cn); 124 } 125 } 126 } 127 } 128 reload()129 public void reload() { 130 loadEnabledServices(); 131 mServices.clear(); 132 final int user = ActivityManager.getCurrentUser(); 133 134 int flags = PackageManager.GET_SERVICES | PackageManager.GET_META_DATA; 135 if (mAddDeviceLockedFlags) { 136 flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE 137 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 138 } 139 140 final PackageManager pmWrapper = mContext.getPackageManager(); 141 List<ResolveInfo> installedServices = pmWrapper.queryIntentServicesAsUser( 142 new Intent(mIntentAction), flags, user); 143 for (ResolveInfo resolveInfo : installedServices) { 144 ServiceInfo info = resolveInfo.serviceInfo; 145 146 if (!mPermission.equals(info.permission)) { 147 Slog.w(mTag, "Skipping " + mNoun + " service " 148 + info.packageName + "/" + info.name 149 + ": it does not require the permission " 150 + mPermission); 151 continue; 152 } 153 if (mValidator != null && !mValidator.test(info)) { 154 continue; 155 } 156 mServices.add(info); 157 } 158 for (Callback callback : mCallbacks) { 159 callback.onServicesReloaded(mServices); 160 } 161 } 162 isEnabled(ComponentName cn)163 public boolean isEnabled(ComponentName cn) { 164 return mEnabledServices.contains(cn); 165 } 166 setEnabled(ComponentName cn, boolean enabled)167 public void setEnabled(ComponentName cn, boolean enabled) { 168 if (enabled) { 169 mEnabledServices.add(cn); 170 } else { 171 mEnabledServices.remove(cn); 172 } 173 saveEnabledServices(); 174 } 175 176 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { 177 @Override 178 public void onChange(boolean selfChange, Uri uri) { 179 reload(); 180 } 181 }; 182 183 private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() { 184 @Override 185 public void onReceive(Context context, Intent intent) { 186 reload(); 187 } 188 }; 189 190 public interface Callback { onServicesReloaded(List<ServiceInfo> services)191 void onServicesReloaded(List<ServiceInfo> services); 192 } 193 194 public static class Builder { 195 private final Context mContext; 196 private String mTag; 197 private String mSetting; 198 private String mIntentAction; 199 private String mPermission; 200 private String mNoun; 201 private boolean mAddDeviceLockedFlags = false; 202 private Predicate mValidator; 203 Builder(Context context)204 public Builder(Context context) { 205 mContext = context; 206 } 207 setTag(String tag)208 public Builder setTag(String tag) { 209 mTag = tag; 210 return this; 211 } 212 setSetting(String setting)213 public Builder setSetting(String setting) { 214 mSetting = setting; 215 return this; 216 } 217 setIntentAction(String intentAction)218 public Builder setIntentAction(String intentAction) { 219 mIntentAction = intentAction; 220 return this; 221 } 222 setPermission(String permission)223 public Builder setPermission(String permission) { 224 mPermission = permission; 225 return this; 226 } 227 setNoun(String noun)228 public Builder setNoun(String noun) { 229 mNoun = noun; 230 return this; 231 } 232 setValidator(Predicate<ServiceInfo> validator)233 public Builder setValidator(Predicate<ServiceInfo> validator) { 234 mValidator = validator; 235 return this; 236 } 237 238 /** 239 * Set to true to add support for both MATCH_DIRECT_BOOT_AWARE and 240 * MATCH_DIRECT_BOOT_UNAWARE flags when querying PackageManager. Required to get results 241 * prior to the user unlocking the device for the first time. 242 */ setAddDeviceLockedFlags(boolean addDeviceLockedFlags)243 public Builder setAddDeviceLockedFlags(boolean addDeviceLockedFlags) { 244 mAddDeviceLockedFlags = addDeviceLockedFlags; 245 return this; 246 } 247 build()248 public ServiceListing build() { 249 return new ServiceListing(mContext, mTag, mSetting, mIntentAction, mPermission, mNoun, 250 mAddDeviceLockedFlags, mValidator); 251 } 252 } 253 } 254