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