1 /* 2 * Copyright (C) 2015 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.settings.utils; 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.text.TextUtils; 34 import android.util.Slog; 35 36 import com.android.settings.utils.ManagedServiceSettings.Config; 37 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.HashSet; 41 import java.util.List; 42 43 public class ServiceListing { 44 private final ContentResolver mContentResolver; 45 private final Context mContext; 46 private final Config mConfig; 47 private final HashSet<ComponentName> mEnabledServices = new HashSet<ComponentName>(); 48 private final List<ServiceInfo> mServices = new ArrayList<ServiceInfo>(); 49 private final List<Callback> mCallbacks = new ArrayList<Callback>(); 50 51 private boolean mListening; 52 53 public ServiceListing(Context context, Config config) { 54 mContext = context; 55 mConfig = config; 56 mContentResolver = context.getContentResolver(); 57 } 58 59 public void addCallback(Callback callback) { 60 mCallbacks.add(callback); 61 } 62 63 public void removeCallback(Callback callback) { 64 mCallbacks.remove(callback); 65 } 66 67 public void setListening(boolean listening) { 68 if (mListening == listening) return; 69 mListening = listening; 70 if (mListening) { 71 // listen for package changes 72 IntentFilter filter = new IntentFilter(); 73 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 74 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 75 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 76 filter.addAction(Intent.ACTION_PACKAGE_REPLACED); 77 filter.addDataScheme("package"); 78 mContext.registerReceiver(mPackageReceiver, filter); 79 mContentResolver.registerContentObserver(Settings.Secure.getUriFor(mConfig.setting), 80 false, mSettingsObserver); 81 } else { 82 mContext.unregisterReceiver(mPackageReceiver); 83 mContentResolver.unregisterContentObserver(mSettingsObserver); 84 } 85 } 86 87 public static int getEnabledServicesCount(Config config, Context context) { 88 final String flat = Settings.Secure.getString(context.getContentResolver(), config.setting); 89 if (flat == null || "".equals(flat)) return 0; 90 final String[] components = flat.split(":"); 91 return components.length; 92 } 93 94 public static int getServicesCount(Config c, PackageManager pm) { 95 return getServices(c, null, pm); 96 } 97 98 protected static int getServices(Config c, List<ServiceInfo> list, PackageManager pm) { 99 int services = 0; 100 if (list != null) { 101 list.clear(); 102 } 103 final int user = ActivityManager.getCurrentUser(); 104 105 List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser( 106 new Intent(c.intentAction), 107 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, 108 user); 109 110 for (int i = 0, count = installedServices.size(); i < count; i++) { 111 ResolveInfo resolveInfo = installedServices.get(i); 112 ServiceInfo info = resolveInfo.serviceInfo; 113 114 if (!c.permission.equals(info.permission)) { 115 Slog.w(c.tag, "Skipping " + c.noun + " service " 116 + info.packageName + "/" + info.name 117 + ": it does not require the permission " 118 + c.permission); 119 continue; 120 } 121 if (list != null) { 122 list.add(info); 123 } 124 services++; 125 } 126 return services; 127 } 128 129 private void saveEnabledServices() { 130 StringBuilder sb = null; 131 for (ComponentName cn : mEnabledServices) { 132 if (sb == null) { 133 sb = new StringBuilder(); 134 } else { 135 sb.append(':'); 136 } 137 sb.append(cn.flattenToString()); 138 } 139 Settings.Secure.putString(mContentResolver, mConfig.setting, 140 sb != null ? sb.toString() : ""); 141 } 142 143 private void loadEnabledServices() { 144 mEnabledServices.clear(); 145 final String flat = Settings.Secure.getString(mContentResolver, mConfig.setting); 146 if (flat != null && !"".equals(flat)) { 147 final String[] names = flat.split(":"); 148 for (int i = 0; i < names.length; i++) { 149 final ComponentName cn = ComponentName.unflattenFromString(names[i]); 150 if (cn != null) { 151 mEnabledServices.add(cn); 152 } 153 } 154 } 155 } 156 157 public List<ServiceInfo> reload() { 158 loadEnabledServices(); 159 getServices(mConfig, mServices, mContext.getPackageManager()); 160 for (Callback callback : mCallbacks) { 161 callback.onServicesReloaded(mServices); 162 } 163 return mServices; 164 } 165 166 public boolean isEnabled(ComponentName cn) { 167 return mEnabledServices.contains(cn); 168 } 169 170 public void setEnabled(ComponentName cn, boolean enabled) { 171 if (enabled) { 172 mEnabledServices.add(cn); 173 } else { 174 mEnabledServices.remove(cn); 175 } 176 saveEnabledServices(); 177 } 178 179 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { 180 @Override 181 public void onChange(boolean selfChange, Uri uri) { 182 reload(); 183 } 184 }; 185 186 private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() { 187 @Override 188 public void onReceive(Context context, Intent intent) { 189 reload(); 190 } 191 }; 192 193 public interface Callback { 194 void onServicesReloaded(List<ServiceInfo> services); 195 } 196 } 197