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 39 /** 40 * Class for managing services matching a given intent and requesting a given permission. 41 */ 42 public class ServiceListing { 43 private final ContentResolver mContentResolver; 44 private final Context mContext; 45 private final String mTag; 46 private final String mSetting; 47 private final String mIntentAction; 48 private final String mPermission; 49 private final String mNoun; 50 private final boolean mAddDeviceLockedFlags; 51 private final HashSet<ComponentName> mEnabledServices = new HashSet<>(); 52 private final List<ServiceInfo> mServices = new ArrayList<>(); 53 private final List<Callback> mCallbacks = new ArrayList<>(); 54 55 private boolean mListening; 56 ServiceListing(Context context, String tag, String setting, String intentAction, String permission, String noun, boolean addDeviceLockedFlags)57 private ServiceListing(Context context, String tag, 58 String setting, String intentAction, String permission, String noun, 59 boolean addDeviceLockedFlags) { 60 mContentResolver = context.getContentResolver(); 61 mContext = context; 62 mTag = tag; 63 mSetting = setting; 64 mIntentAction = intentAction; 65 mPermission = permission; 66 mNoun = noun; 67 mAddDeviceLockedFlags = addDeviceLockedFlags; 68 } 69 addCallback(Callback callback)70 public void addCallback(Callback callback) { 71 mCallbacks.add(callback); 72 } 73 removeCallback(Callback callback)74 public void removeCallback(Callback callback) { 75 mCallbacks.remove(callback); 76 } 77 setListening(boolean listening)78 public void setListening(boolean listening) { 79 if (mListening == listening) return; 80 mListening = listening; 81 if (mListening) { 82 // listen for package changes 83 IntentFilter filter = new IntentFilter(); 84 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 85 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 86 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 87 filter.addAction(Intent.ACTION_PACKAGE_REPLACED); 88 filter.addDataScheme("package"); 89 mContext.registerReceiver(mPackageReceiver, filter); 90 mContentResolver.registerContentObserver(Settings.Secure.getUriFor(mSetting), 91 false, mSettingsObserver); 92 } else { 93 mContext.unregisterReceiver(mPackageReceiver); 94 mContentResolver.unregisterContentObserver(mSettingsObserver); 95 } 96 } 97 saveEnabledServices()98 private void saveEnabledServices() { 99 StringBuilder sb = null; 100 for (ComponentName cn : mEnabledServices) { 101 if (sb == null) { 102 sb = new StringBuilder(); 103 } else { 104 sb.append(':'); 105 } 106 sb.append(cn.flattenToString()); 107 } 108 Settings.Secure.putString(mContentResolver, mSetting, 109 sb != null ? sb.toString() : ""); 110 } 111 loadEnabledServices()112 private void loadEnabledServices() { 113 mEnabledServices.clear(); 114 final String flat = Settings.Secure.getString(mContentResolver, mSetting); 115 if (flat != null && !"".equals(flat)) { 116 final String[] names = flat.split(":"); 117 for (String name : names) { 118 final ComponentName cn = ComponentName.unflattenFromString(name); 119 if (cn != null) { 120 mEnabledServices.add(cn); 121 } 122 } 123 } 124 } 125 reload()126 public void reload() { 127 loadEnabledServices(); 128 mServices.clear(); 129 final int user = ActivityManager.getCurrentUser(); 130 131 int flags = PackageManager.GET_SERVICES | PackageManager.GET_META_DATA; 132 if (mAddDeviceLockedFlags) { 133 flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE 134 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 135 } 136 137 final PackageManager pmWrapper = mContext.getPackageManager(); 138 List<ResolveInfo> installedServices = pmWrapper.queryIntentServicesAsUser( 139 new Intent(mIntentAction), flags, user); 140 141 for (ResolveInfo resolveInfo : installedServices) { 142 ServiceInfo info = resolveInfo.serviceInfo; 143 144 if (!mPermission.equals(info.permission)) { 145 Slog.w(mTag, "Skipping " + mNoun + " service " 146 + info.packageName + "/" + info.name 147 + ": it does not require the permission " 148 + mPermission); 149 continue; 150 } 151 mServices.add(info); 152 } 153 for (Callback callback : mCallbacks) { 154 callback.onServicesReloaded(mServices); 155 } 156 } 157 isEnabled(ComponentName cn)158 public boolean isEnabled(ComponentName cn) { 159 return mEnabledServices.contains(cn); 160 } 161 setEnabled(ComponentName cn, boolean enabled)162 public void setEnabled(ComponentName cn, boolean enabled) { 163 if (enabled) { 164 mEnabledServices.add(cn); 165 } else { 166 mEnabledServices.remove(cn); 167 } 168 saveEnabledServices(); 169 } 170 171 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { 172 @Override 173 public void onChange(boolean selfChange, Uri uri) { 174 reload(); 175 } 176 }; 177 178 private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() { 179 @Override 180 public void onReceive(Context context, Intent intent) { 181 reload(); 182 } 183 }; 184 185 public interface Callback { onServicesReloaded(List<ServiceInfo> services)186 void onServicesReloaded(List<ServiceInfo> services); 187 } 188 189 public static class Builder { 190 private final Context mContext; 191 private String mTag; 192 private String mSetting; 193 private String mIntentAction; 194 private String mPermission; 195 private String mNoun; 196 private boolean mAddDeviceLockedFlags = false; 197 Builder(Context context)198 public Builder(Context context) { 199 mContext = context; 200 } 201 setTag(String tag)202 public Builder setTag(String tag) { 203 mTag = tag; 204 return this; 205 } 206 setSetting(String setting)207 public Builder setSetting(String setting) { 208 mSetting = setting; 209 return this; 210 } 211 setIntentAction(String intentAction)212 public Builder setIntentAction(String intentAction) { 213 mIntentAction = intentAction; 214 return this; 215 } 216 setPermission(String permission)217 public Builder setPermission(String permission) { 218 mPermission = permission; 219 return this; 220 } 221 setNoun(String noun)222 public Builder setNoun(String noun) { 223 mNoun = noun; 224 return this; 225 } 226 227 /** 228 * Set to true to add support for both MATCH_DIRECT_BOOT_AWARE and 229 * MATCH_DIRECT_BOOT_UNAWARE flags when querying PackageManager. Required to get results 230 * prior to the user unlocking the device for the first time. 231 */ setAddDeviceLockedFlags(boolean addDeviceLockedFlags)232 public Builder setAddDeviceLockedFlags(boolean addDeviceLockedFlags) { 233 mAddDeviceLockedFlags = addDeviceLockedFlags; 234 return this; 235 } 236 build()237 public ServiceListing build() { 238 return new ServiceListing(mContext, mTag, mSetting, mIntentAction, mPermission, mNoun, 239 mAddDeviceLockedFlags); 240 } 241 } 242 } 243