1 /* 2 * Copyright (C) 2013 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.systemui.statusbar; 18 19 import android.content.BroadcastReceiver; 20 import android.content.ComponentName; 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.ServiceConnection; 26 import android.content.pm.PackageManager; 27 import android.database.ContentObserver; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.Message; 33 import android.os.RemoteException; 34 import android.os.UserHandle; 35 import android.provider.Settings; 36 import android.util.Log; 37 38 import java.util.Arrays; 39 40 /** 41 * Manages a persistent connection to a service component defined in a secure setting. 42 * 43 * <p>If a valid service component is specified in the secure setting, starts it up and keeps it 44 * running; handling setting changes, package updates, component disabling, and unexpected 45 * process termination. 46 * 47 * <p>Clients can listen for important events using the supplied {@link Callbacks}. 48 */ 49 public class ServiceMonitor { 50 private static final int RECHECK_DELAY = 2000; 51 private static final int WAIT_FOR_STOP = 500; 52 53 public interface Callbacks { 54 /** The service does not exist or failed to bind */ onNoService()55 void onNoService(); 56 /** The service is about to start, this is a chance to perform cleanup and 57 * delay the start if necessary */ onServiceStartAttempt()58 long onServiceStartAttempt(); 59 } 60 61 // internal handler + messages used to serialize access to internal state 62 public static final int MSG_START_SERVICE = 1; 63 public static final int MSG_CONTINUE_START_SERVICE = 2; 64 public static final int MSG_STOP_SERVICE = 3; 65 public static final int MSG_PACKAGE_INTENT = 4; 66 public static final int MSG_CHECK_BOUND = 5; 67 public static final int MSG_SERVICE_DISCONNECTED = 6; 68 69 private final Handler mHandler = new Handler() { 70 public void handleMessage(Message msg) { 71 switch(msg.what) { 72 case MSG_START_SERVICE: 73 startService(); 74 break; 75 case MSG_CONTINUE_START_SERVICE: 76 continueStartService(); 77 break; 78 case MSG_STOP_SERVICE: 79 stopService(); 80 break; 81 case MSG_PACKAGE_INTENT: 82 packageIntent((Intent)msg.obj); 83 break; 84 case MSG_CHECK_BOUND: 85 checkBound(); 86 break; 87 case MSG_SERVICE_DISCONNECTED: 88 serviceDisconnected((ComponentName)msg.obj); 89 break; 90 } 91 } 92 }; 93 94 private final ContentObserver mSettingObserver = new ContentObserver(mHandler) { 95 public void onChange(boolean selfChange) { 96 onChange(selfChange, null); 97 } 98 99 public void onChange(boolean selfChange, Uri uri) { 100 if (mDebug) Log.d(mTag, "onChange selfChange=" + selfChange + " uri=" + uri); 101 ComponentName cn = getComponentNameFromSetting(); 102 if (cn == null && mServiceName == null || cn != null && cn.equals(mServiceName)) { 103 if (mDebug) Log.d(mTag, "skipping no-op restart"); 104 return; 105 } 106 if (mBound) { 107 mHandler.sendEmptyMessage(MSG_STOP_SERVICE); 108 } 109 mHandler.sendEmptyMessageDelayed(MSG_START_SERVICE, WAIT_FOR_STOP); 110 } 111 }; 112 113 private final class SC implements ServiceConnection, IBinder.DeathRecipient { 114 private ComponentName mName; 115 private IBinder mService; 116 onServiceConnected(ComponentName name, IBinder service)117 public void onServiceConnected(ComponentName name, IBinder service) { 118 if (mDebug) Log.d(mTag, "onServiceConnected name=" + name + " service=" + service); 119 mName = name; 120 mService = service; 121 try { 122 service.linkToDeath(this, 0); 123 } catch (RemoteException e) { 124 Log.w(mTag, "Error linking to death", e); 125 } 126 } 127 onServiceDisconnected(ComponentName name)128 public void onServiceDisconnected(ComponentName name) { 129 if (mDebug) Log.d(mTag, "onServiceDisconnected name=" + name); 130 boolean unlinked = mService.unlinkToDeath(this, 0); 131 if (mDebug) Log.d(mTag, " unlinked=" + unlinked); 132 mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_DISCONNECTED, mName)); 133 } 134 binderDied()135 public void binderDied() { 136 if (mDebug) Log.d(mTag, "binderDied"); 137 mHandler.sendMessage(mHandler.obtainMessage(MSG_SERVICE_DISCONNECTED, mName)); 138 } 139 } 140 141 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 142 public void onReceive(Context context, Intent intent) { 143 String pkg = intent.getData().getSchemeSpecificPart(); 144 if (mServiceName != null && mServiceName.getPackageName().equals(pkg)) { 145 mHandler.sendMessage(mHandler.obtainMessage(MSG_PACKAGE_INTENT, intent)); 146 } 147 } 148 }; 149 150 private final String mTag; 151 private final boolean mDebug; 152 153 private final Context mContext; 154 private final String mSettingKey; 155 private final Callbacks mCallbacks; 156 157 private ComponentName mServiceName; 158 private SC mServiceConnection; 159 private boolean mBound; 160 ServiceMonitor(String ownerTag, boolean debug, Context context, String settingKey, Callbacks callbacks)161 public ServiceMonitor(String ownerTag, boolean debug, 162 Context context, String settingKey, Callbacks callbacks) { 163 mTag = ownerTag + ".ServiceMonitor"; 164 mDebug = debug; 165 mContext = context; 166 mSettingKey = settingKey; 167 mCallbacks = callbacks; 168 } 169 start()170 public void start() { 171 // listen for setting changes 172 ContentResolver cr = mContext.getContentResolver(); 173 cr.registerContentObserver(Settings.Secure.getUriFor(mSettingKey), 174 false /*notifyForDescendents*/, mSettingObserver, UserHandle.USER_ALL); 175 176 // listen for package/component changes 177 IntentFilter filter = new IntentFilter(); 178 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 179 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 180 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 181 filter.addDataScheme("package"); 182 mContext.registerReceiver(mBroadcastReceiver, filter); 183 184 mHandler.sendEmptyMessage(MSG_START_SERVICE); 185 } 186 getComponentNameFromSetting()187 private ComponentName getComponentNameFromSetting() { 188 String cn = Settings.Secure.getStringForUser(mContext.getContentResolver(), 189 mSettingKey, UserHandle.USER_CURRENT); 190 return cn == null ? null : ComponentName.unflattenFromString(cn); 191 } 192 193 // everything below is called on the handler 194 packageIntent(Intent intent)195 private void packageIntent(Intent intent) { 196 if (mDebug) Log.d(mTag, "packageIntent intent=" + intent 197 + " extras=" + bundleToString(intent.getExtras())); 198 if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) { 199 mHandler.sendEmptyMessage(MSG_START_SERVICE); 200 } else if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction()) 201 || Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { 202 final PackageManager pm = mContext.getPackageManager(); 203 final boolean serviceEnabled = isPackageAvailable() 204 && pm.getApplicationEnabledSetting(mServiceName.getPackageName()) 205 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED 206 && pm.getComponentEnabledSetting(mServiceName) 207 != PackageManager.COMPONENT_ENABLED_STATE_DISABLED; 208 if (mBound && !serviceEnabled) { 209 stopService(); 210 scheduleCheckBound(); 211 } else if (!mBound && serviceEnabled) { 212 startService(); 213 } 214 } 215 } 216 stopService()217 private void stopService() { 218 if (mDebug) Log.d(mTag, "stopService"); 219 boolean stopped = mContext.stopService(new Intent().setComponent(mServiceName)); 220 if (mDebug) Log.d(mTag, " stopped=" + stopped); 221 mContext.unbindService(mServiceConnection); 222 mBound = false; 223 } 224 startService()225 private void startService() { 226 mServiceName = getComponentNameFromSetting(); 227 if (mDebug) Log.d(mTag, "startService mServiceName=" + mServiceName); 228 if (mServiceName == null) { 229 mBound = false; 230 mCallbacks.onNoService(); 231 } else { 232 long delay = mCallbacks.onServiceStartAttempt(); 233 mHandler.sendEmptyMessageDelayed(MSG_CONTINUE_START_SERVICE, delay); 234 } 235 } 236 continueStartService()237 private void continueStartService() { 238 if (mDebug) Log.d(mTag, "continueStartService"); 239 Intent intent = new Intent().setComponent(mServiceName); 240 try { 241 mServiceConnection = new SC(); 242 mBound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); 243 if (mDebug) Log.d(mTag, "mBound: " + mBound); 244 } catch (Throwable t) { 245 Log.w(mTag, "Error binding to service: " + mServiceName, t); 246 } 247 if (!mBound) { 248 mCallbacks.onNoService(); 249 } 250 } 251 serviceDisconnected(ComponentName serviceName)252 private void serviceDisconnected(ComponentName serviceName) { 253 if (mDebug) Log.d(mTag, "serviceDisconnected serviceName=" + serviceName 254 + " mServiceName=" + mServiceName); 255 if (serviceName.equals(mServiceName)) { 256 mBound = false; 257 scheduleCheckBound(); 258 } 259 } 260 checkBound()261 private void checkBound() { 262 if (mDebug) Log.d(mTag, "checkBound mBound=" + mBound); 263 if (!mBound) { 264 startService(); 265 } 266 } 267 scheduleCheckBound()268 private void scheduleCheckBound() { 269 mHandler.removeMessages(MSG_CHECK_BOUND); 270 mHandler.sendEmptyMessageDelayed(MSG_CHECK_BOUND, RECHECK_DELAY); 271 } 272 bundleToString(Bundle bundle)273 private static String bundleToString(Bundle bundle) { 274 if (bundle == null) return null; 275 StringBuilder sb = new StringBuilder('{'); 276 for (String key : bundle.keySet()) { 277 if (sb.length() > 1) sb.append(','); 278 Object v = bundle.get(key); 279 v = (v instanceof String[]) ? Arrays.asList((String[]) v) : v; 280 sb.append(key).append('=').append(v); 281 } 282 return sb.append('}').toString(); 283 } 284 getComponent()285 public ComponentName getComponent() { 286 return getComponentNameFromSetting(); 287 } 288 setComponent(ComponentName component)289 public void setComponent(ComponentName component) { 290 final String setting = component == null ? null : component.flattenToShortString(); 291 Settings.Secure.putStringForUser(mContext.getContentResolver(), 292 mSettingKey, setting, UserHandle.USER_CURRENT); 293 } 294 isPackageAvailable()295 public boolean isPackageAvailable() { 296 final ComponentName component = getComponent(); 297 if (component == null) return false; 298 try { 299 return mContext.getPackageManager().isPackageAvailable(component.getPackageName()); 300 } catch (RuntimeException e) { 301 Log.w(mTag, "Error checking package availability", e); 302 return false; 303 } 304 } 305 } 306