RunningServiceDetails.java revision c01b0c83fca571229621d16b757a46dc0fae7dfe
1package com.android.settings.applications; 2 3import com.android.settings.R; 4 5import android.app.Activity; 6import android.app.ActivityManager; 7import android.app.PendingIntent; 8import android.content.ActivityNotFoundException; 9import android.content.ComponentName; 10import android.content.Context; 11import android.content.Intent; 12import android.content.IntentSender; 13import android.content.pm.PackageManager; 14import android.content.pm.ProviderInfo; 15import android.content.pm.ServiceInfo; 16import android.content.pm.PackageManager.NameNotFoundException; 17import android.content.res.Resources; 18import android.os.Bundle; 19import android.os.Handler; 20import android.os.HandlerThread; 21import android.os.Looper; 22import android.os.Message; 23import android.util.Log; 24import android.view.LayoutInflater; 25import android.view.View; 26import android.view.ViewGroup; 27import android.widget.LinearLayout; 28import android.widget.TextView; 29 30import java.util.ArrayList; 31import java.util.List; 32 33public class RunningServiceDetails extends Activity { 34 static final String TAG = "RunningServicesDetails"; 35 36 static final String KEY_UID = "uid"; 37 static final String KEY_PROCESS = "process"; 38 39 static final int MSG_UPDATE_TIMES = 1; 40 static final int MSG_UPDATE_CONTENTS = 2; 41 static final int MSG_REFRESH_UI = 3; 42 43 ActivityManager mAm; 44 LayoutInflater mInflater; 45 46 RunningState mState; 47 48 int mUid; 49 String mProcessName; 50 51 RunningState.MergedItem mMergedItem; 52 53 ViewGroup mAllDetails; 54 ViewGroup mSnippet; 55 RunningProcessesView.ActiveItem mSnippetActiveItem; 56 RunningProcessesView.ViewHolder mSnippetViewHolder; 57 58 int mNumServices, mNumProcesses; 59 60 TextView mServicesHeader; 61 TextView mProcessesHeader; 62 final ArrayList<ActiveDetail> mActiveDetails = new ArrayList<ActiveDetail>(); 63 64 class ActiveDetail implements View.OnClickListener { 65 View mRootView; 66 RunningProcessesView.ActiveItem mActiveItem; 67 RunningProcessesView.ViewHolder mViewHolder; 68 PendingIntent mManageIntent; 69 70 public void onClick(View v) { 71 if (mManageIntent != null) { 72 try { 73 startIntentSender(mManageIntent.getIntentSender(), null, 74 Intent.FLAG_ACTIVITY_NEW_TASK 75 | Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 76 Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET, 0); 77 } catch (IntentSender.SendIntentException e) { 78 Log.w(TAG, e); 79 } catch (IllegalArgumentException e) { 80 Log.w(TAG, e); 81 } catch (ActivityNotFoundException e) { 82 Log.w(TAG, e); 83 } 84 } else if (mActiveItem.mItem instanceof RunningState.ServiceItem) { 85 RunningState.ServiceItem si = (RunningState.ServiceItem)mActiveItem.mItem; 86 stopService(new Intent().setComponent(si.mRunningService.service)); 87 if (mMergedItem == null || mMergedItem.mServices.size() <= 1) { 88 // If there was only one service, we are finishing it, 89 // so no reason for the UI to stick around. 90 finish(); 91 } else { 92 if (mBackgroundHandler != null) { 93 mBackgroundHandler.sendEmptyMessage(MSG_UPDATE_CONTENTS); 94 } 95 } 96 } else { 97 // Heavy-weight process. We'll do a force-stop on it. 98 mAm.forceStopPackage(mActiveItem.mItem.mPackageInfo.packageName); 99 finish(); 100 } 101 } 102 } 103 104 StringBuilder mBuilder = new StringBuilder(128); 105 106 HandlerThread mBackgroundThread; 107 final class BackgroundHandler extends Handler { 108 public BackgroundHandler(Looper looper) { 109 super(looper); 110 } 111 112 @Override 113 public void handleMessage(Message msg) { 114 switch (msg.what) { 115 case MSG_UPDATE_CONTENTS: 116 Message cmd = mHandler.obtainMessage(MSG_REFRESH_UI); 117 cmd.arg1 = mState.update(RunningServiceDetails.this, mAm) ? 1 : 0; 118 mHandler.sendMessage(cmd); 119 removeMessages(MSG_UPDATE_CONTENTS); 120 msg = obtainMessage(MSG_UPDATE_CONTENTS); 121 sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY); 122 break; 123 } 124 } 125 }; 126 127 BackgroundHandler mBackgroundHandler; 128 129 final Handler mHandler = new Handler() { 130 @Override 131 public void handleMessage(Message msg) { 132 switch (msg.what) { 133 case MSG_UPDATE_TIMES: 134 if (mSnippetActiveItem != null) { 135 mSnippetActiveItem.updateTime(RunningServiceDetails.this, mBuilder); 136 } 137 for (int i=0; i<mActiveDetails.size(); i++) { 138 mActiveDetails.get(i).mActiveItem.updateTime( 139 RunningServiceDetails.this, mBuilder); 140 } 141 removeMessages(MSG_UPDATE_TIMES); 142 msg = obtainMessage(MSG_UPDATE_TIMES); 143 sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY); 144 break; 145 case MSG_REFRESH_UI: 146 refreshUi(msg.arg1 != 0); 147 break; 148 } 149 } 150 }; 151 152 boolean findMergedItem() { 153 RunningState.MergedItem item = null; 154 ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems(); 155 if (newItems != null) { 156 for (int i=0; i<newItems.size(); i++) { 157 RunningState.MergedItem mi = newItems.get(i); 158 if (mi.mProcess.mUid == mUid 159 && mi.mProcess.mProcessName.equals(mProcessName)) { 160 item = mi; 161 break; 162 } 163 } 164 } 165 if (mMergedItem != item) { 166 mMergedItem = item; 167 return true; 168 } 169 return false; 170 } 171 172 void addServiceDetailsView(RunningState.ServiceItem si, RunningState.MergedItem mi) { 173 if (mNumServices == 0) { 174 mServicesHeader = (TextView)mInflater.inflate(R.layout.separator_label, 175 mAllDetails, false); 176 mServicesHeader.setText(R.string.runningservicedetails_services_title); 177 mAllDetails.addView(mServicesHeader); 178 } 179 mNumServices++; 180 181 RunningState.BaseItem bi = si != null ? si : mi; 182 183 ActiveDetail detail = new ActiveDetail(); 184 View root = mInflater.inflate(R.layout.running_service_details_service, 185 mAllDetails, false); 186 mAllDetails.addView(root); 187 detail.mRootView = root; 188 detail.mViewHolder = new RunningProcessesView.ViewHolder(root); 189 detail.mActiveItem = detail.mViewHolder.bind(mState, bi, mBuilder); 190 191 if (si != null && si.mRunningService.clientLabel != 0) { 192 detail.mManageIntent = mAm.getRunningServiceControlPanel( 193 si.mRunningService.service); 194 } 195 196 TextView description = (TextView)root.findViewById(R.id.comp_description); 197 if (si != null && si.mServiceInfo.descriptionRes != 0) { 198 description.setText(getPackageManager().getText( 199 si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes, 200 si.mServiceInfo.applicationInfo)); 201 } else { 202 if (detail.mManageIntent != null) { 203 try { 204 Resources clientr = getPackageManager().getResourcesForApplication( 205 si.mRunningService.clientPackage); 206 String label = clientr.getString(si.mRunningService.clientLabel); 207 description.setText(getString(R.string.service_manage_description, 208 label)); 209 } catch (PackageManager.NameNotFoundException e) { 210 } 211 } else { 212 description.setText(getText(si != null 213 ? R.string.service_stop_description 214 : R.string.heavy_weight_stop_description)); 215 } 216 } 217 218 View button = root.findViewById(R.id.right_button); 219 button.setOnClickListener(detail); 220 ((TextView)button).setText(getText(detail.mManageIntent != null 221 ? R.string.service_manage : R.string.service_stop)); 222 root.findViewById(R.id.left_button).setVisibility(View.INVISIBLE); 223 224 mActiveDetails.add(detail); 225 } 226 227 void addProcessDetailsView(RunningState.ProcessItem pi, boolean isMain) { 228 if (mNumProcesses == 0) { 229 mProcessesHeader = (TextView)mInflater.inflate(R.layout.separator_label, 230 mAllDetails, false); 231 mProcessesHeader.setText(R.string.runningservicedetails_processes_title); 232 mAllDetails.addView(mProcessesHeader); 233 } 234 mNumProcesses++; 235 236 ActiveDetail detail = new ActiveDetail(); 237 View root = mInflater.inflate(R.layout.running_service_details_process, 238 mAllDetails, false); 239 mAllDetails.addView(root); 240 detail.mRootView = root; 241 detail.mViewHolder = new RunningProcessesView.ViewHolder(root); 242 detail.mActiveItem = detail.mViewHolder.bind(mState, pi, mBuilder); 243 244 TextView description = (TextView)root.findViewById(R.id.comp_description); 245 if (isMain) { 246 description.setText(R.string.main_running_process_description); 247 } else { 248 int textid = 0; 249 CharSequence label = null; 250 ActivityManager.RunningAppProcessInfo rpi = pi.mRunningProcessInfo; 251 final ComponentName comp = rpi.importanceReasonComponent; 252 //Log.i(TAG, "Secondary proc: code=" + rpi.importanceReasonCode 253 // + " pid=" + rpi.importanceReasonPid + " comp=" + comp); 254 switch (rpi.importanceReasonCode) { 255 case ActivityManager.RunningAppProcessInfo.REASON_PROVIDER_IN_USE: 256 textid = R.string.process_provider_in_use_description; 257 List<ProviderInfo> providers = null; 258 if (comp != null) { 259 providers = getPackageManager() 260 .queryContentProviders(comp.getPackageName(), 261 rpi.uid, 0); 262 } 263 if (providers != null) { 264 for (int j=0; j<providers.size(); j++) { 265 ProviderInfo prov = providers.get(j); 266 if (comp.getClassName().equals(prov.name)) { 267 label = RunningState.makeLabel(getPackageManager(), 268 prov.name, prov); 269 break; 270 } 271 } 272 } 273 break; 274 case ActivityManager.RunningAppProcessInfo.REASON_SERVICE_IN_USE: 275 textid = R.string.process_service_in_use_description; 276 if (rpi.importanceReasonComponent != null) { 277 try { 278 ServiceInfo serv = getPackageManager().getServiceInfo( 279 rpi.importanceReasonComponent, 0); 280 label = RunningState.makeLabel(getPackageManager(), 281 serv.name, serv); 282 } catch (NameNotFoundException e) { 283 } 284 } 285 break; 286 } 287 if (textid != 0 && label != null) { 288 description.setText(getString(textid, label)); 289 } 290 } 291 292 mActiveDetails.add(detail); 293 } 294 295 void addDetailViews() { 296 for (int i=mActiveDetails.size()-1; i>=0; i--) { 297 mAllDetails.removeView(mActiveDetails.get(i).mRootView); 298 } 299 mActiveDetails.clear(); 300 301 if (mServicesHeader != null) { 302 mAllDetails.removeView(mServicesHeader); 303 mServicesHeader = null; 304 } 305 306 if (mProcessesHeader != null) { 307 mAllDetails.removeView(mProcessesHeader); 308 mProcessesHeader = null; 309 } 310 311 mNumServices = mNumProcesses = 0; 312 313 if (mMergedItem != null) { 314 for (int i=0; i<mMergedItem.mServices.size(); i++) { 315 addServiceDetailsView(mMergedItem.mServices.get(i), mMergedItem); 316 } 317 318 if (mMergedItem.mServices.size() <= 0) { 319 // This item does not have any services, so it must be 320 // a heavy-weight process... we will put a fake service 321 // entry for it, to allow the user to "stop" it. 322 addServiceDetailsView(null, mMergedItem); 323 } 324 325 for (int i=-1; i<mMergedItem.mOtherProcesses.size(); i++) { 326 RunningState.ProcessItem pi = i < 0 ? mMergedItem.mProcess 327 : mMergedItem.mOtherProcesses.get(i); 328 if (pi.mPid <= 0) { 329 continue; 330 } 331 332 addProcessDetailsView(pi, i < 0); 333 } 334 } 335 } 336 337 void refreshUi(boolean dataChanged) { 338 if (findMergedItem()) { 339 dataChanged = true; 340 } 341 if (dataChanged) { 342 if (mMergedItem != null) { 343 mSnippetActiveItem = mSnippetViewHolder.bind(mState, 344 mMergedItem, mBuilder); 345 } else if (mSnippetActiveItem != null) { 346 // Clear whatever is currently being shown. 347 mSnippetActiveItem.mHolder.size.setText(""); 348 mSnippetActiveItem.mHolder.uptime.setText(""); 349 mSnippetActiveItem.mHolder.description.setText(R.string.no_services); 350 } else { 351 // No merged item, never had one. Nothing to do. 352 finish(); 353 return; 354 } 355 addDetailViews(); 356 } 357 } 358 359 @Override 360 protected void onCreate(Bundle savedInstanceState) { 361 super.onCreate(savedInstanceState); 362 363 mUid = getIntent().getIntExtra(KEY_UID, 0); 364 mProcessName = getIntent().getStringExtra(KEY_PROCESS); 365 366 mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); 367 mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); 368 369 mState = (RunningState)getLastNonConfigurationInstance(); 370 if (mState == null) { 371 mState = new RunningState(); 372 } 373 374 setContentView(R.layout.running_service_details); 375 376 mAllDetails = (ViewGroup)findViewById(R.id.all_details); 377 mSnippet = (ViewGroup)findViewById(R.id.snippet); 378 mSnippet.setBackgroundResource(com.android.internal.R.drawable.title_bar_medium); 379 mSnippet.setPadding(0, mSnippet.getPaddingTop(), 0, mSnippet.getPaddingBottom()); 380 mSnippetViewHolder = new RunningProcessesView.ViewHolder(mSnippet); 381 } 382 383 @Override 384 protected void onPause() { 385 super.onPause(); 386 mHandler.removeMessages(MSG_UPDATE_TIMES); 387 if (mBackgroundThread != null) { 388 mBackgroundThread.quit(); 389 mBackgroundThread = null; 390 mBackgroundHandler = null; 391 } 392 } 393 394 @Override 395 protected void onResume() { 396 super.onResume(); 397 refreshUi(mState.update(this, mAm)); 398 mBackgroundThread = new HandlerThread("RunningServices"); 399 mBackgroundThread.start(); 400 mBackgroundHandler = new BackgroundHandler(mBackgroundThread.getLooper()); 401 mHandler.removeMessages(MSG_UPDATE_TIMES); 402 Message msg = mHandler.obtainMessage(MSG_UPDATE_TIMES); 403 mHandler.sendMessageDelayed(msg, RunningProcessesView.TIME_UPDATE_DELAY); 404 mBackgroundHandler.removeMessages(MSG_UPDATE_CONTENTS); 405 msg = mBackgroundHandler.obtainMessage(MSG_UPDATE_CONTENTS); 406 mBackgroundHandler.sendMessageDelayed(msg, RunningProcessesView.CONTENTS_UPDATE_DELAY); 407 } 408 409 @Override 410 public Object onRetainNonConfigurationInstance() { 411 return mState; 412 } 413 414 @Override 415 protected void onSaveInstanceState(Bundle outState) { 416 super.onSaveInstanceState(outState); 417 } 418} 419